update namespaces

refactor the name of the organisation
This commit is contained in:
Mathias Beaulieu-Duncan 2023-11-04 15:24:56 -04:00
parent 88c86513e9
commit 1c81288895
116 changed files with 1695 additions and 1747 deletions

View File

@ -0,0 +1,14 @@
using System;
namespace OpenHarbor.CQRS.Abstractions.Attributes;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class CommandNameAttribute : Attribute
{
public CommandNameAttribute(string name)
{
Name = name;
}
public string Name { get; }
}

View File

@ -0,0 +1,15 @@
using System;
namespace OpenHarbor.CQRS.Abstractions.Attributes;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class QueryNameAttribute : Attribute
{
public QueryNameAttribute(string name)
{
Name = name;
}
public string Name { get; }
}

View File

@ -0,0 +1,51 @@
using System;
using System.Reflection;
using OpenHarbor.CQRS.Abstractions.Attributes;
namespace OpenHarbor.CQRS.Abstractions.Discovery;
public sealed class CommandMeta : ICommandMeta
{
public CommandMeta(Type commandType, Type serviceType, Type commandResultType)
{
CommandType = commandType;
ServiceType = serviceType;
CommandResultType = commandResultType;
}
public CommandMeta(Type commandType, Type serviceType)
{
CommandType = commandType;
ServiceType = serviceType;
}
private CommandNameAttribute NameAttribute => CommandType.GetCustomAttribute<CommandNameAttribute>();
public string Name
{
get
{
var name = NameAttribute?.Name ?? CommandType.Name.Replace("Command", string.Empty);
return name;
}
}
public Type CommandType { get; }
public Type ServiceType { get; }
public Type CommandResultType { get; }
public string LowerCamelCaseName
{
get
{
if (string.IsNullOrEmpty(Name))
return Name;
var name = Name;
var firstLetter = Char.ToLowerInvariant(name[0]);
var ret = $"{firstLetter}{name.Substring(1)}";
return ret;
}
}
}

View File

@ -0,0 +1,13 @@
using System;
namespace OpenHarbor.CQRS.Abstractions.Discovery;
public interface ICommandMeta
{
string Name { get; }
Type CommandType { get; }
Type ServiceType { get; }
Type CommandResultType { get; }
string LowerCamelCaseName { get; }
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
namespace OpenHarbor.CQRS.Abstractions.Discovery;
public interface IQueryDiscovery
{
IQueryMeta FindQuery(string name);
IQueryMeta FindQuery(Type queryType);
IEnumerable<IQueryMeta> GetQueries();
bool QueryExists(string name);
bool QueryExists(Type queryType);
}
public interface ICommandDiscovery
{
bool CommandExists(string name);
bool CommandExists(Type commandType);
ICommandMeta FindCommand(string name);
ICommandMeta FindCommand(Type commandType);
IEnumerable<ICommandMeta> GetCommands();
}

View File

@ -0,0 +1,13 @@
using System;
namespace OpenHarbor.CQRS.Abstractions.Discovery;
public interface IQueryMeta
{
string Name { get; }
Type QueryType { get; }
Type ServiceType { get; }
Type QueryResultType { get; }
string Category { get; }
string LowerCamelCaseName { get; }
}

View File

@ -0,0 +1,46 @@
using System;
using System.Reflection;
using OpenHarbor.CQRS.Abstractions.Attributes;
namespace OpenHarbor.CQRS.Abstractions.Discovery;
public class QueryMeta : IQueryMeta
{
public QueryMeta(Type queryType, Type serviceType, Type queryResultType)
{
QueryType = queryType;
ServiceType = serviceType;
QueryResultType = queryResultType;
}
protected virtual QueryNameAttribute NameAttribute => QueryType.GetCustomAttribute<QueryNameAttribute>();
public virtual string Name
{
get
{
var name = NameAttribute?.Name ?? QueryType.Name.Replace("Query", string.Empty);
return name;
}
}
public virtual Type QueryType { get; }
public virtual Type ServiceType { get; }
public virtual Type QueryResultType { get; }
public virtual string Category => "BasicQuery";
public string LowerCamelCaseName
{
get
{
if (string.IsNullOrEmpty(Name))
return Name;
var name = Name;
var firstLetter = char.ToLowerInvariant(name[0]);
var ret = $"{firstLetter}{name[1..]}";
return ret;
}
}
}

View File

@ -0,0 +1,16 @@
using System.Threading;
using System.Threading.Tasks;
namespace OpenHarbor.CQRS.Abstractions;
public interface ICommandHandler<in TCommand>
where TCommand : class
{
Task HandleAsync(TCommand command, CancellationToken cancellationToken = default);
}
public interface ICommandHandler<in TCommand, TCommandResult>
where TCommand : class
{
Task<TCommandResult> HandleAsync(TCommand command, CancellationToken cancellationToken = default);
}

View File

@ -0,0 +1,10 @@
using System.Threading;
using System.Threading.Tasks;
namespace OpenHarbor.CQRS.Abstractions;
public interface IQueryHandler<in TQuery, TQueryResult>
where TQuery : class
{
Task<TQueryResult> HandleAsync(TQuery query, CancellationToken cancellationToken = default);
}

View File

@ -1,10 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Copyright>Powered Softwares Inc.</Copyright>
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;amp;r=g&amp;amp;d=retro</PackageIconUrl>
<Company>PoweredSoft</Company>
<Authors>PoweredSoft Team</Authors>
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -0,0 +1,8 @@
namespace OpenHarbor.CQRS.Abstractions.Security;
public enum AuthorizationResult
{
Unauthorized,
Forbidden,
Allowed
}

View File

@ -0,0 +1,10 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace OpenHarbor.CQRS.Abstractions.Security;
public interface ICommandAuthorizationService
{
Task<AuthorizationResult> IsAllowedAsync(Type commandType, CancellationToken cancellationToken = default);
}

View File

@ -0,0 +1,10 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace OpenHarbor.CQRS.Abstractions.Security;
public interface IQueryAuthorizationService
{
Task<AuthorizationResult> IsAllowedAsync(Type queryType, CancellationToken cancellationToken = default);
}

View File

@ -0,0 +1,49 @@
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.CQRS.Abstractions.Discovery;
namespace OpenHarbor.CQRS.Abstractions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddQuery<TQuery, TQueryResult, TQueryHandler>(this IServiceCollection services)
where TQuery : class
where TQueryHandler : class, IQueryHandler<TQuery, TQueryResult>
{
// add handler to DI.
services.AddTransient<IQueryHandler<TQuery, TQueryResult>, TQueryHandler>();
// add for discovery purposes.
var queryMeta = new QueryMeta(typeof(TQuery), typeof(IQueryHandler<TQuery, TQueryResult>), typeof(TQueryResult));
services.AddSingleton<IQueryMeta>(queryMeta);
return services;
}
public static IServiceCollection AddCommand<TCommand, TCommandResult, TCommandHandler>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand, TCommandResult>
{
// add handler to DI.
services.AddTransient<ICommandHandler<TCommand, TCommandResult>, TCommandHandler>();
// add for discovery purposes.
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>), typeof(TCommandResult));
services.AddSingleton<ICommandMeta>(commandMeta);
return services;
}
public static IServiceCollection AddCommand<TCommand, TCommandHandler>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand>
{
// add handler to DI.
services.AddTransient<ICommandHandler<TCommand>, TCommandHandler>();
// add for discovery purposes.
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>));
services.AddSingleton<ICommandMeta>(commandMeta);
return services;
}
}

View File

@ -0,0 +1,8 @@
using System;
namespace OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class CommandControllerIgnoreAttribute : Attribute
{
}

View File

@ -0,0 +1,8 @@
using System;
namespace OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class QueryControllerIgnoreAttribute : Attribute
{
}

View File

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<LangVersion>default</LangVersion>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,38 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using OpenHarbor.CQRS.Abstractions;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
[Produces("application/json")]
[ApiController, Route("api/command/[controller]")]
public class CommandController<TCommand> : Controller
where TCommand : class
{
[HttpPost, CommandControllerAuthorization]
public async Task<IActionResult> Handle([FromServices] ICommandHandler<TCommand> handler,
[FromBody] TCommand command)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted);
return Ok();
}
}
[Produces("application/json")]
[ApiController, Route("api/command/[controller]")]
public class CommandController<TCommand, TTCommandResult> : Controller
where TCommand : class
{
[HttpPost, CommandControllerAuthorization]
public async Task<ActionResult<TTCommandResult>> Handle([FromServices] ICommandHandler<TCommand, TTCommandResult> handler,
[FromBody] TCommand command)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok(await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted));
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using System.Reflection;
using OpenHarbor.CQRS.Abstractions.Security;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
public class CommandControllerAsyncAuthorizationFilter : IAsyncAuthorizationFilter
{
private readonly ICommandAuthorizationService _authorizationService;
public CommandControllerAsyncAuthorizationFilter(IServiceProvider serviceProvider)
{
_authorizationService = serviceProvider.GetService<ICommandAuthorizationService>();
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (_authorizationService == null)
return;
var action = context.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
if (action == null)
throw new Exception("Only Supports controller action descriptor");
var attribute = action.MethodInfo.GetCustomAttribute<CommandControllerAuthorizationAttribute>();
Type commandType;
if (attribute?.CommandType != null)
commandType = attribute.CommandType;
else
commandType = action.ControllerTypeInfo.GenericTypeArguments.First();
var ar = await _authorizationService.IsAllowedAsync(commandType);
if (ar == AuthorizationResult.Forbidden)
context.Result = new StatusCodeResult(403);
else if(ar == AuthorizationResult.Unauthorized)
context.Result = new StatusCodeResult(401);
}
}

View File

@ -0,0 +1,20 @@
using System;
using Microsoft.AspNetCore.Mvc;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
[AttributeUsage(AttributeTargets.Method)]
public class CommandControllerAuthorizationAttribute : TypeFilterAttribute
{
public CommandControllerAuthorizationAttribute() : base(typeof(CommandControllerAsyncAuthorizationFilter))
{
}
public CommandControllerAuthorizationAttribute(Type commandType) : base(typeof(CommandControllerAsyncAuthorizationFilter))
{
CommandType = commandType;
}
public Type CommandType { get; } = null;
}

View File

@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.CQRS.Abstractions.Discovery;
using System;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
public class CommandControllerConvention : IControllerModelConvention
{
private readonly IServiceProvider _serviceProvider;
public CommandControllerConvention(IServiceProvider serviceProvider)
{
this._serviceProvider = serviceProvider;
}
public void Apply(ControllerModel controller)
{
if (!controller.ControllerType.IsGenericType)
return;
if (!controller.ControllerType.Name.Contains("CommandController"))
return;
if (controller.ControllerType.Assembly != typeof(CommandControllerConvention).Assembly)
return;
var genericType = controller.ControllerType.GenericTypeArguments[0];
var commandDiscovery = this._serviceProvider.GetRequiredService<ICommandDiscovery>();
var command = commandDiscovery.FindCommand(genericType);
controller.ControllerName = command.LowerCamelCaseName;
}
}

View File

@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.CQRS.Abstractions.Discovery;
using OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
public class CommandControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
private readonly ServiceProvider _serviceProvider;
public CommandControllerFeatureProvider(ServiceProvider serviceProvider)
{
this._serviceProvider = serviceProvider;
}
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
var commandDiscovery = this._serviceProvider.GetRequiredService<ICommandDiscovery>();
foreach (var f in commandDiscovery.GetCommands())
{
var ignoreAttribute = f.CommandType.GetCustomAttribute<CommandControllerIgnoreAttribute>();
if (ignoreAttribute != null)
continue;
if (f.CommandResultType == null)
{
var controllerType = typeof(CommandController<>).MakeGenericType(f.CommandType);
var controllerTypeInfo = controllerType.GetTypeInfo();
feature.Controllers.Add(controllerTypeInfo);
}
else
{
var controllerType = typeof(CommandController<,>).MakeGenericType(f.CommandType, f.CommandResultType);
var controllerTypeInfo = controllerType.GetTypeInfo();
feature.Controllers.Add(controllerTypeInfo);
}
}
}
}

View File

@ -0,0 +1,6 @@
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
public class CommandControllerOptions
{
}

View File

@ -0,0 +1,27 @@
using System;
using Microsoft.Extensions.DependencyInjection;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
public static class MvcBuilderExtensions
{
public static IMvcBuilder AddPoweredSoftQueries(this IMvcBuilder builder, Action<QueryControllerOptions> configuration = null)
{
var options = new QueryControllerOptions();
configuration?.Invoke(options);
var services = builder.Services;
var serviceProvider = services.BuildServiceProvider();
builder.AddMvcOptions(o => o.Conventions.Add(new QueryControllerConvention(serviceProvider)));
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new QueryControllerFeatureProvider(serviceProvider)));
return builder;
}
public static IMvcBuilder AddPoweredSoftCommands(this IMvcBuilder builder)
{
var services = builder.Services;
var serviceProvider = services.BuildServiceProvider();
builder.AddMvcOptions(o => o.Conventions.Add(new CommandControllerConvention(serviceProvider)));
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new CommandControllerFeatureProvider(serviceProvider)));
return builder;
}
}

View File

@ -0,0 +1,33 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using OpenHarbor.CQRS.Abstractions;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
[Produces("application/json")]
[ApiController, Route("api/query/[controller]")]
public class QueryController<TQuery, TQueryResult> : Controller
where TQuery : class
{
[HttpPost, QueryControllerAuthorization]
public async Task<ActionResult<TQueryResult>> Handle([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
[FromBody] TQuery query)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok(await handler.HandleAsync(query, this.Request.HttpContext.RequestAborted));
}
[HttpGet, QueryControllerAuthorization]
public async Task<ActionResult<TQueryResult>> HandleGet([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
[FromQuery] TQuery query)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok(await handler.HandleAsync(query, this.Request.HttpContext.RequestAborted));
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using System.Reflection;
using OpenHarbor.CQRS.Abstractions.Security;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
public class QueryControllerAsyncAuthorizationFilter : IAsyncAuthorizationFilter
{
private readonly IQueryAuthorizationService _authorizationService;
public QueryControllerAsyncAuthorizationFilter(IServiceProvider serviceProvider)
{
_authorizationService = serviceProvider.GetService<IQueryAuthorizationService>();
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (_authorizationService == null)
return;
var action = context.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
if (action == null)
throw new Exception("Only Supports controller action descriptor");
var attribute = action.MethodInfo.GetCustomAttribute<QueryControllerAuthorizationAttribute>();
Type queryType;
if (attribute?.QueryType != null)
queryType = attribute.QueryType;
else
queryType = action.ControllerTypeInfo.GenericTypeArguments.First();
var ar = await _authorizationService.IsAllowedAsync(queryType);
if (ar == AuthorizationResult.Forbidden)
context.Result = new StatusCodeResult(403);
else if (ar == AuthorizationResult.Unauthorized)
context.Result = new StatusCodeResult(401);
}
}

View File

@ -0,0 +1,20 @@
using System;
using Microsoft.AspNetCore.Mvc;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
[AttributeUsage(AttributeTargets.Method)]
public class QueryControllerAuthorizationAttribute : TypeFilterAttribute
{
public QueryControllerAuthorizationAttribute() : base(typeof(QueryControllerAsyncAuthorizationFilter))
{
}
public QueryControllerAuthorizationAttribute(Type queryType) : base(typeof(QueryControllerAsyncAuthorizationFilter))
{
QueryType = queryType;
}
public Type QueryType { get; }
}

View File

@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.CQRS.Abstractions.Discovery;
using System;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
public class QueryControllerConvention : IControllerModelConvention
{
private readonly IServiceProvider _serviceProvider;
public QueryControllerConvention(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void Apply(ControllerModel controller)
{
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("QueryController") && controller.ControllerType.Assembly == typeof(QueryControllerConvention).Assembly)
{
var genericType = controller.ControllerType.GenericTypeArguments[0];
var queryDiscovery = _serviceProvider.GetRequiredService<IQueryDiscovery>();
var query = queryDiscovery.FindQuery(genericType);
controller.ControllerName = query.LowerCamelCaseName;
}
}
}

View File

@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.CQRS.Abstractions.Discovery;
using OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
public class QueryControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
private readonly ServiceProvider _serviceProvider;
public QueryControllerFeatureProvider(ServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
var queryDiscovery = this._serviceProvider.GetRequiredService<IQueryDiscovery>();
foreach (var f in queryDiscovery.GetQueries())
{
var ignoreAttribute = f.QueryType.GetCustomAttribute<QueryControllerIgnoreAttribute>();
if (ignoreAttribute != null)
continue;
if (f.Category != "BasicQuery")
continue;
var controllerType = typeof(QueryController<,>).MakeGenericType(f.QueryType, f.QueryResultType);
var controllerTypeInfo = controllerType.GetTypeInfo();
feature.Controllers.Add(controllerTypeInfo);
}
}
}

View File

@ -0,0 +1,6 @@
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
public class QueryControllerOptions
{
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenHarbor.CQRS.Abstractions\OpenHarbor.CQRS.Abstractions.csproj" />
<ProjectReference Include="..\OpenHarbor.CQRS.AspNetCore.Abstractions\OpenHarbor.CQRS.AspNetCore.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
public class DynamicQueryInterceptorProvider<TSource, TDestination> : IDynamicQueryInterceptorProvider<TSource, TDestination>
{
private readonly Type[] _types;
public DynamicQueryInterceptorProvider(params Type[] types)
{
_types = types;
}
public IEnumerable<Type> GetInterceptorsTypes()
{
return _types;
}
}

View File

@ -0,0 +1,16 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
public interface IAlterQueryableService<TSource, TDestination>
{
Task<IQueryable<TSource>> AlterQueryableAsync(IQueryable<TSource> query, IDynamicQuery dynamicQuery, CancellationToken cancellationToken = default);
}
public interface IAlterQueryableService<TSource, TDestination, in TParams>
where TParams : class
{
Task<IQueryable<TSource>> AlterQueryableAsync(IQueryable<TSource> query, IDynamicQueryParams<TParams> dynamicQuery, CancellationToken cancellationToken = default);
}

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
using PoweredSoft.DynamicQuery.Core;
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
public interface IDynamicQuery<TSource, TDestination> : IDynamicQuery
where TSource : class
where TDestination : class
{
}
public interface IDynamicQuery<TSource, TDestination, out TParams> : IDynamicQuery<TSource, TDestination>, IDynamicQueryParams<TParams>
where TSource : class
where TDestination : class
where TParams : class
{
}
public interface IDynamicQuery
{
List<IFilter> GetFilters();
List<IGroup> GetGroups();
List<ISort> GetSorts();
List<IAggregate> GetAggregates();
int? GetPage();
int? GetPageSize();
}

View File

@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
public interface IDynamicQueryInterceptorProvider<TSource, TDestination>
{
IEnumerable<Type> GetInterceptorsTypes();
}

View File

@ -0,0 +1,7 @@
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
public interface IDynamicQueryParams<out TParams>
where TParams : class
{
TParams GetParams();
}

View File

@ -0,0 +1,10 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
public interface IQueryableProvider<TSource>
{
Task<IQueryable<TSource>> GetQueryableAsync(object query, CancellationToken cancellationToken = default);
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PoweredSoft.DynamicQuery.Core" Version="3.0.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,68 @@
using System.Collections.Generic;
using System.Linq;
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore;
public class DynamicQuery<TSource, TDestination> : DynamicQuery, IDynamicQuery<TSource, TDestination>
where TSource : class
where TDestination : class
{
}
public class DynamicQuery<TSource, TDestination, TParams> : DynamicQuery, IDynamicQuery<TSource, TDestination, TParams>
where TSource : class
where TDestination : class
where TParams : class
{
public TParams Params { get; set; }
public TParams GetParams()
{
return Params;
}
}
public class DynamicQuery : IDynamicQuery
{
public int? Page { get; set; }
public int? PageSize { get; set; }
public List<Sort> Sorts { get; set; }
public List<DynamicQueryAggregate> Aggregates { get; set; }
public List<Group> Groups { get; set; }
public List<DynamicQueryFilter> Filters { get; set; }
public List<IAggregate> GetAggregates()
{
return Aggregates?.Select(t => t.ToAggregate())?.ToList();//.AsEnumerable<IAggregate>()?.ToList();
}
public List<IFilter> GetFilters()
{
return Filters?.Select(t => t.ToFilter())?.ToList();
}
public List<IGroup> GetGroups()
{
return this.Groups?.AsEnumerable<IGroup>()?.ToList();
}
public int? GetPage()
{
return this.Page;
}
public int? GetPageSize()
{
return this.PageSize;
}
public List<ISort> GetSorts()
{
return this.Sorts?.AsEnumerable<ISort>()?.ToList();
}
}

View File

@ -0,0 +1,21 @@
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
using System;
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore;
public class DynamicQueryAggregate
{
public string Path { get; set; }
public string Type { get; set; }
public IAggregate ToAggregate()
{
return new Aggregate
{
Type = Enum.Parse<AggregateType>(Type),
Path = Path
};
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore;
public class DynamicQueryFilter
{
public List<DynamicQueryFilter> Filters { get; set; }
public bool? And { get; set; }
public string Type { get; set; }
public bool? Not { get; set; }
public string Path { get; set; }
public object Value { get; set; }
[FromQuery(Name ="value")]
public string QueryValue
{
get
{
return null;
}
set
{
Value = value;
}
}
public bool? CaseInsensitive { get; set; }
public IFilter ToFilter()
{
var type = Enum.Parse<FilterType>(Type);
if (type == FilterType.Composite)
{
var compositeFilter = new CompositeFilter
{
And = And,
Type = FilterType.Composite,
Filters = Filters?.Select(t => t.ToFilter())?.ToList() ?? new List<IFilter>()
};
return compositeFilter;
}
object value = Value;
if (Value is JsonElement jsonElement)
{
switch (jsonElement.ValueKind)
{
case JsonValueKind.String:
value = jsonElement.ToString();
break;
case JsonValueKind.Number:
if (jsonElement.ToString().Contains('.'))
value = jsonElement.GetDecimal();
else if (jsonElement.TryGetInt64(out var convertedValue))
value = convertedValue;
break;
case JsonValueKind.True:
value = true;
break;
case JsonValueKind.False:
value = false;
break;
// TODO: Array support
default:
value = null;
break;
}
}
var simpleFilter = new SimpleFilter
{
And = And,
Type = type,
Not = Not,
Path = Path,
Value = value,
CaseInsensitive = CaseInsensitive,
};
return simpleFilter;
}
}

View File

@ -0,0 +1,61 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using OpenHarbor.CQRS.Abstractions;
using OpenHarbor.CQRS.AspNetCore.Mvc;
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
using PoweredSoft.DynamicQuery.Core;
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
[ApiController, Route("api/query/[controller]")]
public class DynamicQueryController<TSource, TDestination> : Controller
where TSource : class
where TDestination : class
{
[HttpPost, QueryControllerAuthorization]
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
[FromBody] DynamicQuery<TSource, TDestination> query,
[FromServices]IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>> queryHandler
)
{
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
return result;
}
[HttpGet, QueryControllerAuthorization]
public async Task<IQueryExecutionResult<TDestination>> HandleGetAsync(
[FromQuery] DynamicQuery<TSource, TDestination> query,
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>> queryHandler
)
{
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
return result;
}
}
[ApiController, Route("api/query/[controller]")]
public class DynamicQueryController<TSource, TDestination, TParams> : Controller
where TSource : class
where TDestination : class
where TParams : class
{
[HttpPost, QueryControllerAuthorization]
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
[FromBody] DynamicQuery<TSource, TDestination, TParams> query,
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>> queryHandler
)
{
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
return result;
}
[HttpGet, QueryControllerAuthorization]
public async Task<IQueryExecutionResult<TDestination>> HandleGetAsync(
[FromQuery] DynamicQuery<TSource, TDestination, TParams> query,
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>> queryHandler
)
{
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
return result;
}
}

View File

@ -0,0 +1,27 @@
using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.CQRS.Abstractions.Discovery;
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
public class DynamicQueryControllerConvention : IControllerModelConvention
{
private readonly IServiceProvider _serviceProvider;
public DynamicQueryControllerConvention(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void Apply(ControllerModel controller)
{
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("DynamicQueryController") && controller.ControllerType.Assembly == typeof(DynamicQueryControllerConvention).Assembly)
{
var genericType = controller.ControllerType.GenericTypeArguments[0];
var queryDiscovery = _serviceProvider.GetRequiredService<IQueryDiscovery>();
var query = queryDiscovery.FindQuery(genericType);
controller.ControllerName = query.LowerCamelCaseName;
}
}
}

View File

@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.CQRS.Abstractions.Discovery;
using OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
using OpenHarbor.CQRS.DynamicQuery.Discover;
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
public class DynamicQueryControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
private readonly ServiceProvider _serviceProvider;
public DynamicQueryControllerFeatureProvider(ServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
var queryDiscovery = _serviceProvider.GetRequiredService<IQueryDiscovery>();
foreach (var queryMeta in queryDiscovery.GetQueries())
{
var ignoreAttribute = queryMeta.QueryType.GetCustomAttribute<QueryControllerIgnoreAttribute>();
if (ignoreAttribute != null)
continue;
if (queryMeta.Category != "DynamicQuery")
continue;
if (queryMeta is DynamicQueryMeta dynamicQueryMeta)
{
if (dynamicQueryMeta.ParamsType == null)
{
var controllerType = typeof(DynamicQueryController<,>).MakeGenericType(queryMeta.QueryType, dynamicQueryMeta.SourceType, dynamicQueryMeta.DestinationType);
var controllerTypeInfo = controllerType.GetTypeInfo();
feature.Controllers.Add(controllerTypeInfo);
}
else
{
var controllerType = typeof(DynamicQueryController<,,>).MakeGenericType(queryMeta.QueryType, dynamicQueryMeta.SourceType, dynamicQueryMeta.DestinationType, dynamicQueryMeta.ParamsType);
var controllerTypeInfo = controllerType.GetTypeInfo();
feature.Controllers.Add(controllerTypeInfo);
}
}
}
}
}

View File

@ -0,0 +1,5 @@
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
public class DynamicQueryControllerOptions
{
}

View File

@ -0,0 +1,19 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore;
public static class MvcBuilderExtensions
{
public static IMvcBuilder AddPoweredSoftDynamicQueries(this IMvcBuilder builder, Action<DynamicQueryControllerOptions> configuration = null)
{
var options = new DynamicQueryControllerOptions();
configuration?.Invoke(options);
var services = builder.Services;
var serviceProvider = services.BuildServiceProvider();
builder.AddMvcOptions(o => o.Conventions.Add(new DynamicQueryControllerConvention(serviceProvider)));
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new DynamicQueryControllerFeatureProvider(serviceProvider)));
return builder;
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenHarbor.CQRS.Abstractions\OpenHarbor.CQRS.Abstractions.csproj" />
<ProjectReference Include="..\OpenHarbor.CQRS.AspNetCore.Abstractions\OpenHarbor.CQRS.AspNetCore.Abstractions.csproj" />
<ProjectReference Include="..\OpenHarbor.CQRS.AspNetCore\OpenHarbor.CQRS.AspNetCore.csproj" />
<ProjectReference Include="..\OpenHarbor.CQRS.DynamicQuery.Abstractions\OpenHarbor.CQRS.DynamicQuery.Abstractions.csproj" />
<ProjectReference Include="..\OpenHarbor.CQRS.DynamicQuery\OpenHarbor.CQRS.DynamicQuery.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,32 @@
using System;
using Pluralize.NET;
using OpenHarbor.CQRS.Abstractions.Discovery;
namespace OpenHarbor.CQRS.DynamicQuery.Discover;
public class DynamicQueryMeta : QueryMeta
{
public DynamicQueryMeta(Type queryType, Type serviceType, Type queryResultType) : base(queryType, serviceType, queryResultType)
{
}
public Type SourceType => QueryType.GetGenericArguments()[0];
public Type DestinationType => QueryType.GetGenericArguments()[1];
public override string Category => "DynamicQuery";
public override string Name
{
get
{
if (OverridableName != null)
return OverridableName;
var pluralizer = new Pluralizer();
return pluralizer.Pluralize(DestinationType.Name);
}
}
public Type ParamsType { get; internal set; }
public string OverridableName { get; internal set; }
}

View File

@ -0,0 +1,67 @@
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
using PoweredSoft.DynamicQuery.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace OpenHarbor.CQRS.DynamicQuery;
public class DynamicQueryHandler<TSource, TDestination>
: DynamicQueryHandlerBase<TSource, TDestination>,
OpenHarbor.CQRS.Abstractions.IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>>
where TSource : class
where TDestination : class
{
public DynamicQueryHandler(IQueryHandlerAsync queryHandlerAsync,
IEnumerable<IQueryableProvider<TSource>> queryableProviders,
IEnumerable<IAlterQueryableService<TSource, TDestination>> alterQueryableServices,
IEnumerable<IDynamicQueryInterceptorProvider<TSource, TDestination>> dynamicQueryInterceptorProviders,
IServiceProvider serviceProvider) : base(queryHandlerAsync, queryableProviders, alterQueryableServices, dynamicQueryInterceptorProviders, serviceProvider)
{
}
public Task<IQueryExecutionResult<TDestination>> HandleAsync(IDynamicQuery<TSource, TDestination> query, CancellationToken cancellationToken = default)
{
return ProcessQueryAsync(query, cancellationToken);
}
}
public class DynamicQueryHandler<TSource, TDestination, TParams>
: DynamicQueryHandlerBase<TSource, TDestination>,
OpenHarbor.CQRS.Abstractions.IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>>
where TSource : class
where TDestination : class
where TParams : class
{
private readonly IEnumerable<IAlterQueryableService<TSource, TDestination, TParams>> alterQueryableServicesWithParams;
public DynamicQueryHandler(IQueryHandlerAsync queryHandlerAsync,
IEnumerable<IQueryableProvider<TSource>> queryableProviders,
IEnumerable<IAlterQueryableService<TSource, TDestination>> alterQueryableServices,
IEnumerable<IAlterQueryableService<TSource, TDestination, TParams>> alterQueryableServicesWithParams,
IEnumerable<IDynamicQueryInterceptorProvider<TSource, TDestination>> dynamicQueryInterceptorProviders,
IServiceProvider serviceProvider) : base(queryHandlerAsync, queryableProviders, alterQueryableServices, dynamicQueryInterceptorProviders, serviceProvider)
{
this.alterQueryableServicesWithParams = alterQueryableServicesWithParams;
}
protected override async Task<IQueryable<TSource>> AlterSourceAsync(IQueryable<TSource> source, IDynamicQuery query, CancellationToken cancellationToken)
{
source = await base.AlterSourceAsync(source, query, cancellationToken);
if (query is IDynamicQueryParams<TParams> withParams)
{
foreach (var it in alterQueryableServicesWithParams)
source = await it.AlterQueryableAsync(source, withParams, cancellationToken);
}
return source;
}
public Task<IQueryExecutionResult<TDestination>> HandleAsync(IDynamicQuery<TSource, TDestination, TParams> query, CancellationToken cancellationToken = default)
{
return this.ProcessQueryAsync(query, cancellationToken);
}
}

View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
namespace OpenHarbor.CQRS.DynamicQuery;
public abstract class DynamicQueryHandlerBase<TSource, TDestination>
where TSource : class
where TDestination : class
{
private readonly IQueryHandlerAsync _queryHandlerAsync;
private readonly IEnumerable<IQueryableProvider<TSource>> _queryableProviders;
private readonly IEnumerable<IAlterQueryableService<TSource, TDestination>> _alterQueryableServices;
private readonly IEnumerable<IDynamicQueryInterceptorProvider<TSource, TDestination>> _dynamicQueryInterceptorProviders;
private readonly IServiceProvider _serviceProvider;
public DynamicQueryHandlerBase(IQueryHandlerAsync queryHandlerAsync,
IEnumerable<IQueryableProvider<TSource>> queryableProviders,
IEnumerable<IAlterQueryableService<TSource, TDestination>> alterQueryableServices,
IEnumerable<IDynamicQueryInterceptorProvider<TSource, TDestination>> dynamicQueryInterceptorProviders,
IServiceProvider serviceProvider)
{
_queryHandlerAsync = queryHandlerAsync;
_queryableProviders = queryableProviders;
_alterQueryableServices = alterQueryableServices;
_dynamicQueryInterceptorProviders = dynamicQueryInterceptorProviders;
_serviceProvider = serviceProvider;
}
protected virtual Task<IQueryable<TSource>> GetQueryableAsync(IDynamicQuery query, CancellationToken cancellationToken = default)
{
if (_queryableProviders.Any())
return _queryableProviders.ElementAt(0).GetQueryableAsync(query, cancellationToken);
throw new Exception($"You must provide a QueryableProvider<TSource> for {typeof(TSource).Name}");
}
public virtual IQueryExecutionOptions GetQueryExecutionOptions(IQueryable<TSource> source, IDynamicQuery query)
{
return new QueryExecutionOptions();
}
public virtual IEnumerable<IQueryInterceptor> GetInterceptors()
{
var types = _dynamicQueryInterceptorProviders.SelectMany(t => t.GetInterceptorsTypes()).Distinct();
foreach (var type in types)
yield return _serviceProvider.GetService(type) as IQueryInterceptor;
}
protected async Task<IQueryExecutionResult<TDestination>> ProcessQueryAsync(IDynamicQuery query, CancellationToken cancellationToken = default)
{
var source = await GetQueryableAsync(query, cancellationToken);
source = await AlterSourceAsync(source, query, cancellationToken);
var options = GetQueryExecutionOptions(source, query);
var interceptors = GetInterceptors();
foreach (var interceptor in interceptors)
_queryHandlerAsync.AddInterceptor(interceptor);
var criteria = CreateCriteriaFromQuery(query);
var result = await _queryHandlerAsync.ExecuteAsync<TSource, TDestination>(source, criteria, options, cancellationToken);
return result;
}
protected virtual async Task<IQueryable<TSource>> AlterSourceAsync(IQueryable<TSource> source, IDynamicQuery query, CancellationToken cancellationToken)
{
foreach (var t in _alterQueryableServices)
source = await t.AlterQueryableAsync(source, query, cancellationToken);
return source;
}
protected virtual IQueryCriteria CreateCriteriaFromQuery(IDynamicQuery query)
{
var criteria = new QueryCriteria
{
Page = query?.GetPage(),
PageSize = query?.GetPageSize(),
Filters = query?.GetFilters() ?? new List<IFilter>(),
Sorts = query?.GetSorts() ?? new List<ISort>(),
Groups = query?.GetGroups() ?? new List<IGroup>(),
Aggregates = query?.GetAggregates() ?? new List<IAggregate>()
};
return criteria;
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Pluralize.NET" Version="1.0.2" />
<PackageReference Include="PoweredSoft.DynamicQuery" Version="3.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenHarbor.CQRS.DynamicQuery.Abstractions\OpenHarbor.CQRS.DynamicQuery.Abstractions.csproj" />
<ProjectReference Include="..\OpenHarbor.CQRS\OpenHarbor.CQRS.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,175 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenHarbor.CQRS.Abstractions;
using OpenHarbor.CQRS.Abstractions.Discovery;
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
using OpenHarbor.CQRS.DynamicQuery.Discover;
using PoweredSoft.DynamicQuery.Core;
namespace OpenHarbor.CQRS.DynamicQuery;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddDynamicQuery<TSourceAndDestination>(this IServiceCollection services, string name = null)
where TSourceAndDestination : class
=> AddDynamicQuery<TSourceAndDestination, TSourceAndDestination>(services, name: name);
public static IServiceCollection AddDynamicQuery<TSource, TDestination>(this IServiceCollection services, string name = null)
where TSource : class
where TDestination : class
{
// add query handler.
services.AddTransient<IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>>, DynamicQueryHandler<TSource, TDestination>>();
// add for discovery purposes.
var queryType = typeof(IDynamicQuery<TSource, TDestination>);
var resultType = typeof(IQueryExecutionResult<TDestination>);
var serviceType = typeof(DynamicQueryHandler<TSource, TDestination>);
var queryMeta = new DynamicQueryMeta(queryType, serviceType, resultType)
{
OverridableName = name
};
services.AddSingleton<IQueryMeta>(queryMeta);
return services;
}
public static IServiceCollection AddDynamicQueryWithProvider<TSource, TQueryableProvider>(this IServiceCollection services, string name = null)
where TQueryableProvider : class, IQueryableProvider<TSource>
where TSource : class
{
services.AddTransient<IQueryableProvider<TSource>, TQueryableProvider>()
.AddDynamicQuery<TSource>(name: name);
return services;
}
public static IServiceCollection AddDynamicQueryWithParamsAndProvider<TSource, TParams, TQueryableProvider>(this IServiceCollection services, string name = null)
where TQueryableProvider : class, IQueryableProvider<TSource>
where TParams : class
where TSource : class
{
services.AddTransient<IQueryableProvider<TSource>, TQueryableProvider>()
.AddDynamicQueryWithParams<TSource, TParams>(name: name);
return services;
}
public static IServiceCollection AddDynamicQueryWithParams<TSourceAndDestination, TParams>(this IServiceCollection services, string name = null)
where TSourceAndDestination : class
where TParams : class
=> AddDynamicQueryWithParams<TSourceAndDestination, TSourceAndDestination, TParams>(services, name: name);
public static IServiceCollection AddDynamicQueryWithParams<TSource, TDestination, TParams>(this IServiceCollection services, string name = null)
where TSource : class
where TDestination : class
where TParams : class
{
// add query handler.
services.AddTransient<IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>>, DynamicQueryHandler<TSource, TDestination, TParams>>();
// add for discovery purposes.
var queryType = typeof(IDynamicQuery<TSource, TDestination, TParams>);
var resultType = typeof(IQueryExecutionResult<TDestination>);
var serviceType = typeof(DynamicQueryHandler<TSource, TDestination>);
var queryMeta = new DynamicQueryMeta(queryType, serviceType, resultType)
{
// params type.
ParamsType = typeof(TParams),
OverridableName = name
};
services.AddSingleton<IQueryMeta>(queryMeta);
return services;
}
public static IServiceCollection AddAlterQueryable<TSourceAndDestination, TService>(this IServiceCollection services)
where TService : class, IAlterQueryableService<TSourceAndDestination, TSourceAndDestination>
{
return services.AddTransient<IAlterQueryableService<TSourceAndDestination, TSourceAndDestination>, TService>();
}
public static IServiceCollection AddAlterQueryable<TSource, TDestination, TService>(this IServiceCollection services)
where TService : class, IAlterQueryableService<TSource, TDestination>
{
return services.AddTransient<IAlterQueryableService<TSource, TDestination>, TService>();
}
public static IServiceCollection AddAlterQueryableWithParams<TSourceAndTDestination, TParams, TService>
(this IServiceCollection services)
where TParams : class
where TService : class, IAlterQueryableService<TSourceAndTDestination, TSourceAndTDestination, TParams>
{
return services.AddTransient<IAlterQueryableService< TSourceAndTDestination, TSourceAndTDestination, TParams>, TService>();
}
public static IServiceCollection AddAlterQueryableWithParams<TSource, TDestination, TParams, TService>
(this IServiceCollection services)
where TParams : class
where TService : class, IAlterQueryableService<TSource, TDestination, TParams>
{
return services.AddTransient<IAlterQueryableService<TSource, TDestination, TParams>, TService>();
}
public static IServiceCollection AddDynamicQueryInterceptor<TSource, TDestination, TInterceptor>(this IServiceCollection services)
where TInterceptor : class, IQueryInterceptor
{
services.TryAddTransient<TInterceptor>();
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(TInterceptor)));
}
public static IServiceCollection AddDynamicQueryInterceptors<TSource, TDestination, T1, T2>(this IServiceCollection services)
where T1 : class, IQueryInterceptor
where T2 : class, IQueryInterceptor
{
services.TryAddTransient<T1>();
services.TryAddTransient<T2>();
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(T1), typeof(T2)));
}
public static IServiceCollection AddDynamicQueryInterceptors<TSource, TDestination, T1, T2, T3>(this IServiceCollection services)
where T1 : class, IQueryInterceptor
where T2 : class, IQueryInterceptor
where T3 : class, IQueryInterceptor
{
services.TryAddTransient<T1>();
services.TryAddTransient<T2>();
services.TryAddTransient<T3>();
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(T1), typeof(T2), typeof(T3)));
}
public static IServiceCollection AddDynamicQueryInterceptors<TSource, TDestination, T1, T2, T3, T4>(this IServiceCollection services)
where T1 : class, IQueryInterceptor
where T2 : class, IQueryInterceptor
where T3 : class, IQueryInterceptor
where T4 : class, IQueryInterceptor
{
services.TryAddTransient<T1>();
services.TryAddTransient<T2>();
services.TryAddTransient<T3>();
services.TryAddTransient<T4>();
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(T1), typeof(T2), typeof(T3), typeof(T4)));
}
public static IServiceCollection AddDynamicQueryInterceptors<TSource, TDestination, T1, T2, T3, T4, T5>(this IServiceCollection services)
where T1 : class, IQueryInterceptor
where T2 : class, IQueryInterceptor
where T3 : class, IQueryInterceptor
where T4 : class, IQueryInterceptor
where T5 : class, IQueryInterceptor
{
services.TryAddTransient<T1>();
services.TryAddTransient<T2>();
services.TryAddTransient<T3>();
services.TryAddTransient<T4>();
services.TryAddTransient<T5>();
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)));
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="10.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenHarbor.CQRS\OpenHarbor.CQRS.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,43 @@
using FluentValidation;
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.CQRS.Abstractions;
namespace OpenHarbor.CQRS.FluentValidation;
public static class ServiceCollectionExtensions
{
private static IServiceCollection AddFluentValidator<T, TValidator>(this IServiceCollection services)
where TValidator : class, IValidator<T>
{
services.AddTransient<IValidator<T>, TValidator>();
return services;
}
public static IServiceCollection AddCommand<TCommand, TCommandHandler, TValidator>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand>
where TValidator : class, IValidator<TCommand>
{
return services.AddCommand<TCommand, TCommandHandler>()
.AddFluentValidator<TCommand, TValidator>();
}
public static IServiceCollection AddCommand<TCommand, TCommandResult, TCommandHandler, TValidator>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand, TCommandResult>
where TValidator : class, IValidator<TCommand>
{
return services.AddCommand<TCommand, TCommandResult, TCommandHandler>()
.AddFluentValidator<TCommand, TValidator>();
}
public static IServiceCollection AddQuery<TQuery, TQueryResult, TQueryHandler, TValidator>(this IServiceCollection services)
where TQuery : class
where TQueryHandler : class, IQueryHandler<TQuery, TQueryResult>
where TValidator : class, IValidator<TQuery>
{
services.AddQuery<TQuery, TQueryResult, TQueryHandler>()
.AddFluentValidator<TQuery, TValidator>();
return services;
}
}

View File

@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30907.101
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.Abstractions", "PoweredSoft.CQRS.Abstractions\PoweredSoft.CQRS.Abstractions.csproj", "{ED78E19D-31D4-4783-AE9E-2844A8541277}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.Abstractions", "OpenHarbor.CQRS.Abstractions\OpenHarbor.CQRS.Abstractions.csproj", "{ED78E19D-31D4-4783-AE9E-2844A8541277}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS", "PoweredSoft.CQRS\PoweredSoft.CQRS.csproj", "{7069B98F-8736-4114-8AF5-1ACE094E6238}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS", "OpenHarbor.CQRS\OpenHarbor.CQRS.csproj", "{7069B98F-8736-4114-8AF5-1ACE094E6238}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.AspNetCore", "PoweredSoft.CQRS.AspNetCore\PoweredSoft.CQRS.AspNetCore.csproj", "{A1D577E5-61BD-4E25-B2C8-1005C1D7665B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.AspNetCore", "OpenHarbor.CQRS.AspNetCore\OpenHarbor.CQRS.AspNetCore.csproj", "{A1D577E5-61BD-4E25-B2C8-1005C1D7665B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.AspNetCore.Abstractions", "PoweredSoft.CQRS.AspNetCore.Abstractions\PoweredSoft.CQRS.AspNetCore.Abstractions.csproj", "{4C466827-31D3-4081-A751-C2FC7C381D7E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.AspNetCore.Abstractions", "OpenHarbor.CQRS.AspNetCore.Abstractions\OpenHarbor.CQRS.AspNetCore.Abstractions.csproj", "{4C466827-31D3-4081-A751-C2FC7C381D7E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{617BA357-1A1F-40C5-B19A-A65A960E6142}"
ProjectSection(SolutionItems) = preProject
@ -17,13 +17,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.DynamicQuery", "PoweredSoft.CQRS.DynamicQuery\PoweredSoft.CQRS.DynamicQuery.csproj", "{A38CE930-191F-417C-B5BE-8CC62DB47513}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.DynamicQuery", "OpenHarbor.CQRS.DynamicQuery\OpenHarbor.CQRS.DynamicQuery.csproj", "{A38CE930-191F-417C-B5BE-8CC62DB47513}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.DynamicQuery.Abstractions", "PoweredSoft.CQRS.DynamicQuery.Abstractions\PoweredSoft.CQRS.DynamicQuery.Abstractions.csproj", "{60B5E255-77B8-48E0-AE8F-04E8332970F9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.DynamicQuery.AspNetCore", "OpenHarbor.CQRS.DynamicQuery.AspNetCore\OpenHarbor.CQRS.DynamicQuery.AspNetCore.csproj", "{0829B99A-0A20-4CAC-A91E-FB67E18444DE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.DynamicQuery.AspNetCore", "PoweredSoft.CQRS.DynamicQuery.AspNetCore\PoweredSoft.CQRS.DynamicQuery.AspNetCore.csproj", "{0829B99A-0A20-4CAC-A91E-FB67E18444DE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.FluentValidation", "OpenHarbor.CQRS.FluentValidation\OpenHarbor.CQRS.FluentValidation.csproj", "{70BD37C4-7497-474D-9A40-A701203971D8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.FluentValidation", "PoweredSoft.CQRS.FluentValidation\PoweredSoft.CQRS.FluentValidation.csproj", "{70BD37C4-7497-474D-9A40-A701203971D8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenHarbor.CQRS.DynamicQuery.Abstractions", "OpenHarbor.CQRS.DynamicQuery.Abstractions\OpenHarbor.CQRS.DynamicQuery.Abstractions.csproj", "{8B9F8ACE-10EA-4215-9776-DE29EC93B020}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -51,10 +51,6 @@ Global
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Release|Any CPU.Build.0 = Release|Any CPU
{60B5E255-77B8-48E0-AE8F-04E8332970F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60B5E255-77B8-48E0-AE8F-04E8332970F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60B5E255-77B8-48E0-AE8F-04E8332970F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60B5E255-77B8-48E0-AE8F-04E8332970F9}.Release|Any CPU.Build.0 = Release|Any CPU
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -63,6 +59,10 @@ Global
{70BD37C4-7497-474D-9A40-A701203971D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70BD37C4-7497-474D-9A40-A701203971D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70BD37C4-7497-474D-9A40-A701203971D8}.Release|Any CPU.Build.0 = Release|Any CPU
{8B9F8ACE-10EA-4215-9776-DE29EC93B020}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B9F8ACE-10EA-4215-9776-DE29EC93B020}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B9F8ACE-10EA-4215-9776-DE29EC93B020}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B9F8ACE-10EA-4215-9776-DE29EC93B020}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenHarbor.CQRS.Abstractions.Discovery;
namespace OpenHarbor.CQRS.Discovery;
public sealed class CommandDiscovery : ICommandDiscovery
{
private readonly IEnumerable<ICommandMeta> _commandMetas;
public CommandDiscovery(IEnumerable<ICommandMeta> commandMetas)
{
_commandMetas = commandMetas;
}
public IEnumerable<ICommandMeta> GetCommands() => _commandMetas;
public ICommandMeta FindCommand(string name) => _commandMetas.FirstOrDefault(t => t.Name == name);
public ICommandMeta FindCommand(Type commandType) => _commandMetas.FirstOrDefault(t => t.CommandType == commandType);
public bool CommandExists(string name) => _commandMetas.Any(t => t.Name == name);
public bool CommandExists(Type commandType) => _commandMetas.Any(t => t.CommandType == commandType);
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenHarbor.CQRS.Abstractions.Discovery;
namespace OpenHarbor.CQRS.Discovery;
public sealed class QueryDiscovery : IQueryDiscovery
{
private readonly IEnumerable<IQueryMeta> _queryMetas;
public QueryDiscovery(IEnumerable<IQueryMeta> queryMetas)
{
_queryMetas = queryMetas;
}
public IEnumerable<IQueryMeta> GetQueries() => _queryMetas;
public IQueryMeta FindQuery(string name) => _queryMetas.FirstOrDefault(t => t.Name == name);
public IQueryMeta FindQuery(Type queryType) => _queryMetas.FirstOrDefault(t => t.QueryType == queryType);
public bool QueryExists(string name) => _queryMetas.Any(t => t.Name == name);
public bool QueryExists(Type queryType) => _queryMetas.Any(t => t.QueryType == queryType);
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\OpenHarbor.CQRS.Abstractions\OpenHarbor.CQRS.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenHarbor.CQRS.Abstractions.Discovery;
using OpenHarbor.CQRS.Discovery;
namespace OpenHarbor.CQRS;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddDefaultQueryDiscovery(this IServiceCollection services)
{
services.TryAddTransient<IQueryDiscovery, QueryDiscovery>();
return services;
}
public static IServiceCollection AddDefaultCommandDiscovery(this IServiceCollection services)
{
services.TryAddTransient<ICommandDiscovery, CommandDiscovery>();
return services;
}
}

View File

@ -1,15 +0,0 @@
using System;
namespace PoweredSoft.CQRS.Abstractions.Attributes
{
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class CommandNameAttribute : Attribute
{
public CommandNameAttribute(string name)
{
Name = name;
}
public string Name { get; }
}
}

View File

@ -1,15 +0,0 @@
using System;
namespace PoweredSoft.CQRS.Abstractions.Attributes
{
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class QueryNameAttribute : Attribute
{
public QueryNameAttribute(string name)
{
Name = name;
}
public string Name { get; }
}
}

View File

@ -1,51 +0,0 @@
using PoweredSoft.CQRS.Abstractions.Attributes;
using System;
using System.Reflection;
namespace PoweredSoft.CQRS.Abstractions.Discovery
{
public class CommandMeta : ICommandMeta
{
public CommandMeta(Type commandType, Type serviceType, Type commandResultType)
{
CommandType = commandType;
ServiceType = serviceType;
CommandResultType = commandResultType;
}
public CommandMeta(Type commandType, Type serviceType)
{
CommandType = commandType;
ServiceType = serviceType;
}
protected virtual CommandNameAttribute NameAttribute => CommandType.GetCustomAttribute<CommandNameAttribute>();
public virtual string Name
{
get
{
var name = NameAttribute?.Name ?? CommandType.Name.Replace("Command", string.Empty);
return name;
}
}
public virtual Type CommandType { get; }
public virtual Type ServiceType { get; }
public virtual Type CommandResultType { get; }
public string LowerCamelCaseName
{
get
{
if (string.IsNullOrEmpty(Name))
return Name;
var name = Name;
var firstLetter = Char.ToLowerInvariant(name[0]);
var ret = $"{firstLetter}{name.Substring(1)}";
return ret;
}
}
}
}

View File

@ -1,13 +0,0 @@
using System;
namespace PoweredSoft.CQRS.Abstractions.Discovery
{
public interface ICommandMeta
{
string Name { get; }
Type CommandType { get; }
Type ServiceType { get; }
Type CommandResultType { get; }
string LowerCamelCaseName { get; }
}
}

View File

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
namespace PoweredSoft.CQRS.Abstractions.Discovery
{
public interface IQueryDiscovery
{
IQueryMeta FindQuery(string name);
IQueryMeta FindQuery(Type queryType);
IEnumerable<IQueryMeta> GetQueries();
bool QueryExists(string name);
bool QueryExists(Type queryType);
}
public interface ICommandDiscovery
{
bool CommandExists(string name);
bool CommandExists(Type commandType);
ICommandMeta FindCommand(string name);
ICommandMeta FindCommand(Type commandType);
IEnumerable<ICommandMeta> GetCommands();
}
}

View File

@ -1,14 +0,0 @@
using System;
namespace PoweredSoft.CQRS.Abstractions.Discovery
{
public interface IQueryMeta
{
string Name { get; }
Type QueryType { get; }
Type ServiceType { get; }
Type QueryResultType { get; }
string Category { get; }
string LowerCamelCaseName { get; }
}
}

View File

@ -1,46 +0,0 @@
using PoweredSoft.CQRS.Abstractions.Attributes;
using System;
using System.Reflection;
namespace PoweredSoft.CQRS.Abstractions.Discovery
{
public class QueryMeta : IQueryMeta
{
public QueryMeta(Type queryType, Type serviceType, Type queryResultType)
{
QueryType = queryType;
ServiceType = serviceType;
QueryResultType = queryResultType;
}
protected virtual QueryNameAttribute NameAttribute => QueryType.GetCustomAttribute<QueryNameAttribute>();
public virtual string Name
{
get
{
var name = NameAttribute?.Name ?? QueryType.Name.Replace("Query", string.Empty);
return name;
}
}
public virtual Type QueryType { get; }
public virtual Type ServiceType { get; }
public virtual Type QueryResultType { get; }
public virtual string Category => "BasicQuery";
public string LowerCamelCaseName
{
get
{
if (string.IsNullOrEmpty(Name))
return Name;
var name = Name;
var firstLetter = Char.ToLowerInvariant(name[0]);
var ret = $"{firstLetter}{name.Substring(1)}";
return ret;
}
}
}
}

View File

@ -1,17 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
namespace PoweredSoft.CQRS.Abstractions
{
public interface ICommandHandler<in TCommand>
where TCommand : class
{
Task HandleAsync(TCommand command, CancellationToken cancellationToken = default);
}
public interface ICommandHandler<in TCommand, TCommandResult>
where TCommand : class
{
Task<TCommandResult> HandleAsync(TCommand command, CancellationToken cancellationToken = default);
}
}

View File

@ -1,11 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
namespace PoweredSoft.CQRS.Abstractions
{
public interface IQueryHandler<in TQuery, TQueryResult>
where TQuery : class
{
Task<TQueryResult> HandleAsync(TQuery query, CancellationToken cancellationToken = default);
}
}

View File

@ -1,9 +0,0 @@
namespace PoweredSoft.CQRS.Abstractions.Security
{
public enum AuthorizationResult
{
Unauthorized,
Forbidden,
Allowed
}
}

View File

@ -1,11 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace PoweredSoft.CQRS.Abstractions.Security
{
public interface ICommandAuthorizationService
{
Task<AuthorizationResult> IsAllowedAsync(Type commandType, CancellationToken cancellationToken = default);
}
}

View File

@ -1,12 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace PoweredSoft.CQRS.Abstractions.Security
{
public interface IQueryAuthorizationService
{
Task<AuthorizationResult> IsAllowedAsync(Type queryType, CancellationToken cancellationToken = default);
}
}

View File

@ -1,50 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using PoweredSoft.CQRS.Abstractions.Discovery;
namespace PoweredSoft.CQRS.Abstractions
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddQuery<TQuery, TQueryResult, TQueryHandler>(this IServiceCollection services)
where TQuery : class
where TQueryHandler : class, IQueryHandler<TQuery, TQueryResult>
{
// add handler to DI.
services.AddTransient<IQueryHandler<TQuery, TQueryResult>, TQueryHandler>();
// add for discovery purposes.
var queryMeta = new QueryMeta(typeof(TQuery), typeof(IQueryHandler<TQuery, TQueryResult>), typeof(TQueryResult));
services.AddSingleton<IQueryMeta>(queryMeta);
return services;
}
public static IServiceCollection AddCommand<TCommand, TCommandResult, TCommandHandler>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand, TCommandResult>
{
// add handler to DI.
services.AddTransient<ICommandHandler<TCommand, TCommandResult>, TCommandHandler>();
// add for discovery purposes.
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>), typeof(TCommandResult));
services.AddSingleton<ICommandMeta>(commandMeta);
return services;
}
public static IServiceCollection AddCommand<TCommand, TCommandHandler>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand>
{
// add handler to DI.
services.AddTransient<ICommandHandler<TCommand>, TCommandHandler>();
// add for discovery purposes.
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>));
services.AddSingleton<ICommandMeta>(commandMeta);
return services;
}
}
}

View File

@ -1,9 +0,0 @@
using System;
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
{
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class CommandControllerIgnoreAttribute : Attribute
{
}
}

View File

@ -1,9 +0,0 @@
using System;
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
{
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class QueryControllerIgnoreAttribute : Attribute
{
}
}

View File

@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Copyright>Powered Softwares Inc.</Copyright>
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;amp;r=g&amp;amp;d=retro</PackageIconUrl>
<Company>PoweredSoft</Company>
<Authors>PoweredSoft Team</Authors>
</PropertyGroup>
</Project>

View File

@ -1,39 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using PoweredSoft.CQRS.Abstractions;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
[Produces("application/json")]
[ApiController, Route("api/command/[controller]")]
public class CommandController<TCommand> : Controller
where TCommand : class
{
[HttpPost, CommandControllerAuthorization]
public async Task<IActionResult> Handle([FromServices] ICommandHandler<TCommand> handler,
[FromBody] TCommand command)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted);
return Ok();
}
}
[Produces("application/json")]
[ApiController, Route("api/command/[controller]")]
public class CommandController<TCommand, TTCommandResult> : Controller
where TCommand : class
{
[HttpPost, CommandControllerAuthorization]
public async Task<ActionResult<TTCommandResult>> Handle([FromServices] ICommandHandler<TCommand, TTCommandResult> handler,
[FromBody] TCommand command)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok(await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted));
}
}
}

View File

@ -1,44 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using System.Reflection;
using PoweredSoft.CQRS.Abstractions.Security;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
public class CommandControllerAsyncAuthorizationFilter : IAsyncAuthorizationFilter
{
private readonly ICommandAuthorizationService authorizationService;
public CommandControllerAsyncAuthorizationFilter(IServiceProvider serviceProvider)
{
authorizationService = serviceProvider.GetService<ICommandAuthorizationService>();
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (authorizationService == null)
return;
var action = context.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
if (action == null)
throw new Exception("Only Supports controller action descriptor");
var attribute = action.MethodInfo.GetCustomAttribute<CommandControllerAuthorizationAttribute>();
Type commandType;
if (attribute?.CommandType != null)
commandType = attribute.CommandType;
else
commandType = action.ControllerTypeInfo.GenericTypeArguments.First();
var ar = await authorizationService.IsAllowedAsync(commandType);
if (ar == AuthorizationResult.Forbidden)
context.Result = new StatusCodeResult(403);
else if(ar == AuthorizationResult.Unauthorized)
context.Result = new StatusCodeResult(401);
}
}
}

View File

@ -1,21 +0,0 @@
using System;
using Microsoft.AspNetCore.Mvc;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
[AttributeUsage(AttributeTargets.Method)]
public class CommandControllerAuthorizationAttribute : TypeFilterAttribute
{
public CommandControllerAuthorizationAttribute() : base(typeof(CommandControllerAsyncAuthorizationFilter))
{
}
public CommandControllerAuthorizationAttribute(Type commandType) : base(typeof(CommandControllerAsyncAuthorizationFilter))
{
CommandType = commandType;
}
public Type CommandType { get; } = null;
}
}

View File

@ -1,34 +0,0 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.DependencyInjection;
using PoweredSoft.CQRS.Abstractions.Discovery;
using System;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
public class CommandControllerConvention : IControllerModelConvention
{
private readonly IServiceProvider serviceProvider;
public CommandControllerConvention(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public void Apply(ControllerModel controller)
{
if (!controller.ControllerType.IsGenericType)
return;
if (!controller.ControllerType.Name.Contains("CommandController"))
return;
if (controller.ControllerType.Assembly != typeof(CommandControllerConvention).Assembly)
return;
var genericType = controller.ControllerType.GenericTypeArguments[0];
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
var command = commandDiscovery.FindCommand(genericType);
controller.ControllerName = command.LowerCamelCaseName;
}
}
}

View File

@ -1,44 +0,0 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using PoweredSoft.CQRS.Abstractions.Discovery;
using PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
public class CommandControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
private readonly ServiceProvider serviceProvider;
public CommandControllerFeatureProvider(ServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
foreach (var f in commandDiscovery.GetCommands())
{
var ignoreAttribute = f.CommandType.GetCustomAttribute<CommandControllerIgnoreAttribute>();
if (ignoreAttribute != null)
continue;
if (f.CommandResultType == null)
{
var controllerType = typeof(CommandController<>).MakeGenericType(f.CommandType);
var controllerTypeInfo = controllerType.GetTypeInfo();
feature.Controllers.Add(controllerTypeInfo);
}
else
{
var controllerType = typeof(CommandController<,>).MakeGenericType(f.CommandType, f.CommandResultType);
var controllerTypeInfo = controllerType.GetTypeInfo();
feature.Controllers.Add(controllerTypeInfo);
}
}
}
}
}

View File

@ -1,7 +0,0 @@
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
public class CommandControllerOptions
{
}
}

View File

@ -1,34 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using PoweredSoft.CQRS.Abstractions;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
[Produces("application/json")]
[ApiController, Route("api/query/[controller]")]
public class QueryController<TQuery, TQueryResult> : Controller
where TQuery : class
{
[HttpPost, QueryControllerAuthorization]
public async Task<ActionResult<TQueryResult>> Handle([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
[FromBody] TQuery query)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok(await handler.HandleAsync(query, this.Request.HttpContext.RequestAborted));
}
[HttpGet, QueryControllerAuthorization]
public async Task<ActionResult<TQueryResult>> HandleGet([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
[FromQuery] TQuery query)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok(await handler.HandleAsync(query, this.Request.HttpContext.RequestAborted));
}
}
}

View File

@ -1,44 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using System.Reflection;
using PoweredSoft.CQRS.Abstractions.Security;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
public class QueryControllerAsyncAuthorizationFilter : IAsyncAuthorizationFilter
{
private readonly IQueryAuthorizationService authorizationService;
public QueryControllerAsyncAuthorizationFilter(IServiceProvider serviceProvider)
{
authorizationService = serviceProvider.GetService<IQueryAuthorizationService>();
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (authorizationService == null)
return;
var action = context.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
if (action == null)
throw new Exception("Only Supports controller action descriptor");
var attribute = action.MethodInfo.GetCustomAttribute<QueryControllerAuthorizationAttribute>();
Type queryType;
if (attribute?.QueryType != null)
queryType = attribute.QueryType;
else
queryType = action.ControllerTypeInfo.GenericTypeArguments.First();
var ar = await authorizationService.IsAllowedAsync(queryType);
if (ar == AuthorizationResult.Forbidden)
context.Result = new StatusCodeResult(403);
else if (ar == AuthorizationResult.Unauthorized)
context.Result = new StatusCodeResult(401);
}
}
}

View File

@ -1,21 +0,0 @@
using System;
using Microsoft.AspNetCore.Mvc;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
[AttributeUsage(AttributeTargets.Method)]
public class QueryControllerAuthorizationAttribute : TypeFilterAttribute
{
public QueryControllerAuthorizationAttribute() : base(typeof(QueryControllerAsyncAuthorizationFilter))
{
}
public QueryControllerAuthorizationAttribute(Type queryType) : base(typeof(QueryControllerAsyncAuthorizationFilter))
{
QueryType = queryType;
}
public Type QueryType { get; } = null;
}
}

View File

@ -1,28 +0,0 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.DependencyInjection;
using PoweredSoft.CQRS.Abstractions.Discovery;
using System;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
public class QueryControllerConvention : IControllerModelConvention
{
private readonly IServiceProvider serviceProvider;
public QueryControllerConvention(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public void Apply(ControllerModel controller)
{
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("QueryController") && controller.ControllerType.Assembly == typeof(QueryControllerConvention).Assembly)
{
var genericType = controller.ControllerType.GenericTypeArguments[0];
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
var query = queryDiscovery.FindQuery(genericType);
controller.ControllerName = query.LowerCamelCaseName;
}
}
}
}

View File

@ -1,38 +0,0 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using PoweredSoft.CQRS.Abstractions.Discovery;
using PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
public class QueryControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
private readonly ServiceProvider serviceProvider;
public QueryControllerFeatureProvider(ServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
foreach (var f in queryDiscovery.GetQueries())
{
var ignoreAttribute = f.QueryType.GetCustomAttribute<QueryControllerIgnoreAttribute>();
if (ignoreAttribute != null)
continue;
if (f.Category != "BasicQuery")
continue;
var controllerType = typeof(QueryController<,>).MakeGenericType(f.QueryType, f.QueryResultType);
var controllerTypeInfo = controllerType.GetTypeInfo();
feature.Controllers.Add(controllerTypeInfo);
}
}
}
}

View File

@ -1,7 +0,0 @@
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
public class QueryControllerOptions
{
}
}

View File

@ -1,29 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Text;
namespace PoweredSoft.CQRS.AspNetCore.Mvc
{
public static class MvcBuilderExtensions
{
public static IMvcBuilder AddPoweredSoftQueries(this IMvcBuilder builder, Action<QueryControllerOptions> configuration = null)
{
var options = new QueryControllerOptions();
configuration?.Invoke(options);
var services = builder.Services;
var serviceProvider = services.BuildServiceProvider();
builder.AddMvcOptions(o => o.Conventions.Add(new QueryControllerConvention(serviceProvider)));
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new QueryControllerFeatureProvider(serviceProvider)));
return builder;
}
public static IMvcBuilder AddPoweredSoftCommands(this IMvcBuilder builder)
{
var services = builder.Services;
var serviceProvider = services.BuildServiceProvider();
builder.AddMvcOptions(o => o.Conventions.Add(new CommandControllerConvention(serviceProvider)));
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new CommandControllerFeatureProvider(serviceProvider)));
return builder;
}
}
}

View File

@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Copyright>Powered Softwares Inc.</Copyright>
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;amp;r=g&amp;amp;d=retro</PackageIconUrl>
<Company>PoweredSoft</Company>
<Authors>PoweredSoft Team</Authors>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PoweredSoft.CQRS.Abstractions\PoweredSoft.CQRS.Abstractions.csproj" />
<ProjectReference Include="..\PoweredSoft.CQRS.AspNetCore.Abstractions\PoweredSoft.CQRS.AspNetCore.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
{
public class DynamicQueryInterceptorProvider<TSource, TDestination> : IDynamicQueryInterceptorProvider<TSource, TDestination>
{
private readonly Type[] types;
public DynamicQueryInterceptorProvider(params Type[] types)
{
this.types = types;
}
public IEnumerable<Type> GetInterceptorsTypes()
{
return types;
}
}
}

View File

@ -1,16 +0,0 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
{
public interface IAlterQueryableService<TSource, TDestination>
{
Task<IQueryable<TSource>> AlterQueryableAsync(IQueryable<TSource> query, IDynamicQuery dynamicQuery, CancellationToken cancellationToken = default);
}
public interface IAlterQueryableService<TSource, TDestination, TParams>
where TParams : class
{
Task<IQueryable<TSource>> AlterQueryableAsync(IQueryable<TSource> query, IDynamicQueryParams<TParams> dynamicQuery, CancellationToken cancellationToken = default);
}
}

View File

@ -1,30 +0,0 @@
using System.Collections.Generic;
using PoweredSoft.DynamicQuery.Core;
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
{
public interface IDynamicQuery<TSource, TDestination> : IDynamicQuery
where TSource : class
where TDestination : class
{
}
public interface IDynamicQuery<TSource, TDestination, out TParams> : IDynamicQuery<TSource, TDestination>, IDynamicQueryParams<TParams>
where TSource : class
where TDestination : class
where TParams : class
{
}
public interface IDynamicQuery
{
List<IFilter> GetFilters();
List<IGroup> GetGroups();
List<ISort> GetSorts();
List<IAggregate> GetAggregates();
int? GetPage();
int? GetPageSize();
}
}

View File

@ -1,10 +0,0 @@
using System;
using System.Collections.Generic;
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
{
public interface IDynamicQueryInterceptorProvider<TSource, TDestination>
{
IEnumerable<Type> GetInterceptorsTypes();
}
}

View File

@ -1,8 +0,0 @@
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
{
public interface IDynamicQueryParams<out TParams>
where TParams : class
{
TParams GetParams();
}
}

View File

@ -1,11 +0,0 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
{
public interface IQueryableProvider<TSource>
{
Task<IQueryable<TSource>> GetQueryableAsync(object query, CancellationToken cancellationToken = default);
}
}

View File

@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Copyright>Powered Softwares Inc.</Copyright>
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;amp;r=g&amp;amp;d=retro</PackageIconUrl>
<Company>PoweredSoft</Company>
<Authors>PoweredSoft Team</Authors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PoweredSoft.DynamicQuery.Core" Version="3.0.1" />
</ItemGroup>
</Project>

View File

@ -1,69 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore
{
public class DynamicQuery<TSource, TDestination> : DynamicQuery, IDynamicQuery<TSource, TDestination>
where TSource : class
where TDestination : class
{
}
public class DynamicQuery<TSource, TDestination, TParams> : DynamicQuery, IDynamicQuery<TSource, TDestination, TParams>
where TSource : class
where TDestination : class
where TParams : class
{
public TParams Params { get; set; }
public TParams GetParams()
{
return Params;
}
}
public class DynamicQuery : IDynamicQuery
{
public int? Page { get; set; }
public int? PageSize { get; set; }
public List<Sort> Sorts { get; set; }
public List<DynamicQueryAggregate> Aggregates { get; set; }
public List<Group> Groups { get; set; }
public List<DynamicQueryFilter> Filters { get; set; }
public List<IAggregate> GetAggregates()
{
return Aggregates?.Select(t => t.ToAggregate())?.ToList();//.AsEnumerable<IAggregate>()?.ToList();
}
public List<IFilter> GetFilters()
{
return Filters?.Select(t => t.ToFilter())?.ToList();
}
public List<IGroup> GetGroups()
{
return this.Groups?.AsEnumerable<IGroup>()?.ToList();
}
public int? GetPage()
{
return this.Page;
}
public int? GetPageSize()
{
return this.PageSize;
}
public List<ISort> GetSorts()
{
return this.Sorts?.AsEnumerable<ISort>()?.ToList();
}
}
}

View File

@ -1,22 +0,0 @@
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
using System;
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore
{
public class DynamicQueryAggregate
{
public string Path { get; set; }
public string Type { get; set; }
public IAggregate ToAggregate()
{
return new Aggregate
{
Type = Enum.Parse<AggregateType>(Type),
Path = Path
};
}
}
}

View File

@ -1,89 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore
{
public class DynamicQueryFilter
{
public List<DynamicQueryFilter> Filters { get; set; }
public bool? And { get; set; }
public string Type { get; set; }
public bool? Not { get; set; }
public string Path { get; set; }
public object Value { get; set; }
[FromQuery(Name ="value")]
public string QueryValue
{
get
{
return null;
}
set
{
Value = value;
}
}
public bool? CaseInsensitive { get; set; }
public IFilter ToFilter()
{
var type = Enum.Parse<FilterType>(Type);
if (type == FilterType.Composite)
{
var compositeFilter = new CompositeFilter
{
And = And,
Type = FilterType.Composite,
Filters = Filters?.Select(t => t.ToFilter())?.ToList() ?? new List<IFilter>()
};
return compositeFilter;
}
object value = Value;
if (Value is JsonElement jsonElement)
{
switch (jsonElement.ValueKind)
{
case JsonValueKind.String:
value = jsonElement.ToString();
break;
case JsonValueKind.Number:
if (jsonElement.ToString().Contains('.'))
value = jsonElement.GetDecimal();
else if (jsonElement.TryGetInt64(out var convertedValue))
value = convertedValue;
break;
case JsonValueKind.True:
value = true;
break;
case JsonValueKind.False:
value = false;
break;
// TODO: Array support
default:
value = null;
break;
}
}
var simpleFilter = new SimpleFilter
{
And = And,
Type = type,
Not = Not,
Path = Path,
Value = value,
CaseInsensitive = CaseInsensitive,
};
return simpleFilter;
}
}
}

View File

@ -1,62 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using PoweredSoft.CQRS.Abstractions;
using PoweredSoft.CQRS.AspNetCore.Mvc;
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
using PoweredSoft.DynamicQuery.Core;
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore.Mvc
{
[ApiController, Route("api/query/[controller]")]
public class DynamicQueryController<TSource, TDestination> : Controller
where TSource : class
where TDestination : class
{
[HttpPost, QueryControllerAuthorization]
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
[FromBody] DynamicQuery<TSource, TDestination> query,
[FromServices]IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>> queryHandler
)
{
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
return result;
}
[HttpGet, QueryControllerAuthorization]
public async Task<IQueryExecutionResult<TDestination>> HandleGetAsync(
[FromQuery] DynamicQuery<TSource, TDestination> query,
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>> queryHandler
)
{
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
return result;
}
}
[ApiController, Route("api/query/[controller]")]
public class DynamicQueryController<TSource, TDestination, TParams> : Controller
where TSource : class
where TDestination : class
where TParams : class
{
[HttpPost, QueryControllerAuthorization]
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
[FromBody] DynamicQuery<TSource, TDestination, TParams> query,
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>> queryHandler
)
{
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
return result;
}
[HttpGet, QueryControllerAuthorization]
public async Task<IQueryExecutionResult<TDestination>> HandleGetAsync(
[FromQuery] DynamicQuery<TSource, TDestination, TParams> query,
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>> queryHandler
)
{
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
return result;
}
}
}

Some files were not shown because too many files have changed in this diff Show More