dynamic query support.
This commit is contained in:
parent
8175dc5f3d
commit
ca307194db
54
Demo/AsyncProvider/InMemoryQueryableHandler.cs
Normal file
54
Demo/AsyncProvider/InMemoryQueryableHandler.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using PoweredSoft.Data.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Demo.AsyncProvider
|
||||
{
|
||||
public class InMemoryQueryableHandler : IAsyncQueryableHandlerService
|
||||
{
|
||||
public Task<bool> AnyAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(queryable.Any(predicate));
|
||||
}
|
||||
|
||||
public Task<bool> AnyAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(queryable.Any());
|
||||
}
|
||||
|
||||
public bool CanHandle<T>(IQueryable<T> queryable)
|
||||
{
|
||||
var result = queryable is EnumerableQuery<T>;
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<int> CountAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(queryable.Count());
|
||||
}
|
||||
|
||||
public Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(queryable.FirstOrDefault());
|
||||
}
|
||||
|
||||
public Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(queryable.FirstOrDefault(predicate));
|
||||
}
|
||||
|
||||
public Task<long> LongCountAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(queryable.LongCount());
|
||||
}
|
||||
|
||||
public Task<List<T>> ToListAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(queryable.ToList());
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="9.5.0" />
|
||||
<PackageReference Include="PoweredSoft.Data" Version="2.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.6.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" />
|
||||
@ -13,6 +14,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PoweredSoft.CQRS.AspNetCore\PoweredSoft.CQRS.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\PoweredSoft.CQRS.DynamicQuery.Abstractions\PoweredSoft.CQRS.DynamicQuery.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\PoweredSoft.CQRS.DynamicQuery.AspNetCore\PoweredSoft.CQRS.DynamicQuery.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\PoweredSoft.CQRS.DynamicQuery\PoweredSoft.CQRS.DynamicQuery.csproj" />
|
||||
<ProjectReference Include="..\PoweredSoft.CQRS\PoweredSoft.CQRS.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
29
Demo/DynamicQueries/Contact.cs
Normal file
29
Demo/DynamicQueries/Contact.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Demo.DynamicQueries
|
||||
{
|
||||
public class Contact
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
}
|
||||
|
||||
public class ContactQueryableProvider : IQueryableProvider<Contact>
|
||||
{
|
||||
public Task<IQueryable<Contact>> GetQueryableAsync(object query, CancellationToken cancelllationToken = default)
|
||||
{
|
||||
var ret = new List<Contact>
|
||||
{
|
||||
new Contact { Id = 1, DisplayName = "David L"},
|
||||
new Contact { Id = 2, DisplayName = "John Doe"}
|
||||
};
|
||||
|
||||
return Task.FromResult(ret.AsQueryable());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using Demo.AsyncProvider;
|
||||
using Demo.Commands;
|
||||
using Demo.DynamicQueries;
|
||||
using Demo.Queries;
|
||||
using FluentValidation;
|
||||
using FluentValidation.AspNetCore;
|
||||
@ -13,6 +15,12 @@ using Microsoft.Extensions.Logging;
|
||||
using PoweredSoft.CQRS;
|
||||
using PoweredSoft.CQRS.Abstractions;
|
||||
using PoweredSoft.CQRS.AspNetCore.Mvc;
|
||||
using PoweredSoft.CQRS.DynamicQuery;
|
||||
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
|
||||
using PoweredSoft.CQRS.DynamicQuery.AspNetCore;
|
||||
using PoweredSoft.Data;
|
||||
using PoweredSoft.Data.Core;
|
||||
using PoweredSoft.DynamicQuery;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -33,18 +41,30 @@ namespace Demo
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
AddQueries(services);
|
||||
AddDynamicQueries(services);
|
||||
AddCommands(services);
|
||||
|
||||
services.AddTransient<IAsyncQueryableHandlerService, InMemoryQueryableHandler>();
|
||||
services.AddPoweredSoftDataServices();
|
||||
services.AddPoweredSoftDynamicQuery();
|
||||
|
||||
services.AddPoweredSoftCQRS();
|
||||
services
|
||||
.AddControllers()
|
||||
.AddPoweredSoftQueries()
|
||||
.AddPoweredSoftCommands()
|
||||
.AddPoweredSoftDynamicQueries()
|
||||
.AddFluentValidation();
|
||||
|
||||
services.AddSwaggerGen();
|
||||
}
|
||||
|
||||
private void AddDynamicQueries(IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IQueryableProvider<Contact>, ContactQueryableProvider>();
|
||||
services.AddDynamicQuery<Contact, Contact>();
|
||||
}
|
||||
|
||||
private void AddCommands(IServiceCollection services)
|
||||
{
|
||||
services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler>();
|
||||
|
@ -11,5 +11,6 @@ namespace PoweredSoft.CQRS.Abstractions.Discovery
|
||||
Type QueryType { get; }
|
||||
Type ServiceType { get; }
|
||||
Type QueryResultType { get; }
|
||||
string Category { get; }
|
||||
}
|
||||
}
|
||||
|
@ -27,5 +27,6 @@ namespace PoweredSoft.CQRS.Abstractions.Discovery
|
||||
public virtual Type QueryType { get; }
|
||||
public virtual Type ServiceType { get; }
|
||||
public virtual Type QueryResultType { get; }
|
||||
public virtual string Category => "BasicQuery";
|
||||
}
|
||||
}
|
||||
|
@ -5,23 +5,23 @@ using System;
|
||||
|
||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||
{
|
||||
public class QueryControllerConvention : IControllerModelConvention
|
||||
public class CommandControllerConvention : IControllerModelConvention
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public QueryControllerConvention(IServiceProvider serviceProvider)
|
||||
public CommandControllerConvention(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)
|
||||
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("CommandController") && controller.ControllerType.Assembly == typeof(CommandControllerConvention).Assembly)
|
||||
{
|
||||
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||
var query = queryDiscovery.FindQuery(genericType);
|
||||
controller.ControllerName = query.Name;
|
||||
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
|
||||
var command = commandDiscovery.FindCommand(genericType);
|
||||
controller.ControllerName = command.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,23 +5,23 @@ using System;
|
||||
|
||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||
{
|
||||
public class CommandControllerConvention : IControllerModelConvention
|
||||
public class QueryControllerConvention : IControllerModelConvention
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public CommandControllerConvention(IServiceProvider serviceProvider)
|
||||
public QueryControllerConvention(IServiceProvider serviceProvider)
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public void Apply(ControllerModel controller)
|
||||
{
|
||||
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("CommandController") && controller.ControllerType.Assembly == typeof(CommandControllerConvention).Assembly)
|
||||
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("QueryController") && controller.ControllerType.Assembly == typeof(QueryControllerConvention).Assembly)
|
||||
{
|
||||
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
|
||||
var command = commandDiscovery.FindCommand(genericType);
|
||||
controller.ControllerName = command.Name;
|
||||
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||
var query = queryDiscovery.FindQuery(genericType);
|
||||
controller.ControllerName = query.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,9 @@ namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||
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);
|
||||
|
25
PoweredSoft.CQRS.DynamicQuery.Abstractions/IDynamicQuery.cs
Normal file
25
PoweredSoft.CQRS.DynamicQuery.Abstractions/IDynamicQuery.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using PoweredSoft.DynamicQuery.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
|
||||
{
|
||||
public interface IDynamicQuery<TSource, TDestination> : IDynamicQuery
|
||||
where TSource : class
|
||||
where TDestination : class
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public interface IDynamicQuery
|
||||
|
||||
{
|
||||
List<IFilter> GetFilters();
|
||||
List<IGroup> GetGroups();
|
||||
List<ISort> GetSorts();
|
||||
List<IAggregate> GetAggregates();
|
||||
int? GetPage();
|
||||
int? GetPageSize();
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
|
||||
{
|
||||
public interface IDynamicQueryParams<TParams>
|
||||
where TParams : class
|
||||
{
|
||||
TParams GetParams();
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
|
||||
{
|
||||
public interface IQueryableProvider<TSource>
|
||||
{
|
||||
Task<IQueryable<TSource>> GetQueryableAsync(object query, CancellationToken cancelllationToken = default);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="PoweredSoft.DynamicQuery.Core" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
71
PoweredSoft.CQRS.DynamicQuery.AspNetCore/DynamicQuery.cs
Normal file
71
PoweredSoft.CQRS.DynamicQuery.AspNetCore/DynamicQuery.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
|
||||
using PoweredSoft.DynamicQuery;
|
||||
using PoweredSoft.DynamicQuery.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
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>, IDynamicQueryParams<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<Aggregate> Aggregates { get; set; }
|
||||
public List<Group> Groups { get; set; }
|
||||
public List<DynamicQueryFilter> Filters { get; set; }
|
||||
|
||||
|
||||
public List<IAggregate> GetAggregates()
|
||||
{
|
||||
return Aggregates?.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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
using PoweredSoft.DynamicQuery;
|
||||
using PoweredSoft.DynamicQuery.Core;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore
|
||||
{
|
||||
public class DynamicQueryFilter
|
||||
{
|
||||
public List<DynamicQueryFilter> Filters { get; set; }
|
||||
public bool? And { get; set; }
|
||||
public FilterType Type { get; set; }
|
||||
public bool? Not { get; set; }
|
||||
public string Path { get; set; }
|
||||
public object Value { get; set; }
|
||||
|
||||
public IFilter ToFilter()
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (jsonElement.ValueKind == JsonValueKind.String)
|
||||
value = jsonElement.ToString();
|
||||
else if (jsonElement.ValueKind == JsonValueKind.Number && jsonElement.TryGetInt64(out var l))
|
||||
value = l;
|
||||
else if (jsonElement.ValueKind == JsonValueKind.True)
|
||||
value = true;
|
||||
else if (jsonElement.ValueKind == JsonValueKind.False)
|
||||
value = false;
|
||||
else if (jsonElement.ValueKind == JsonValueKind.Array)
|
||||
throw new System.Exception("TODO");
|
||||
else
|
||||
value = null;
|
||||
}
|
||||
|
||||
var simpleFilter = new SimpleFilter
|
||||
{
|
||||
And = And,
|
||||
Type = Type,
|
||||
Not = Not,
|
||||
Path = Path,
|
||||
Value = value
|
||||
};
|
||||
return simpleFilter;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
|
||||
using PoweredSoft.DynamicQuery.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore.Mvc
|
||||
{
|
||||
[ApiController, Route("api/query/[controller]")]
|
||||
public class DynamicQueryController<TUnderlyingQuery, TSource, TDestination> : Controller
|
||||
where TSource : class
|
||||
where TDestination : class
|
||||
{
|
||||
[HttpPost]
|
||||
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
|
||||
[FromBody] DynamicQuery<TSource, TDestination> query,
|
||||
[FromServices]PoweredSoft.CQRS.Abstractions.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<TUnderlyingQuery, TSource, TDestination, TParams> : Controller
|
||||
where TSource : class
|
||||
where TDestination : class
|
||||
where TParams : class
|
||||
{
|
||||
[HttpPost]
|
||||
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
|
||||
[FromBody] DynamicQuery<TSource, TDestination, TParams> query,
|
||||
[FromServices] PoweredSoft.CQRS.Abstractions.IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>> queryHandler
|
||||
)
|
||||
{
|
||||
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PoweredSoft.CQRS.Abstractions.Discovery;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore.Mvc
|
||||
{
|
||||
public class DynamicQueryControllerConvention : IControllerModelConvention
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public DynamicQueryControllerConvention(IServiceProvider serviceProvider)
|
||||
{
|
||||
this.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 = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||
var query = queryDiscovery.FindQuery(genericType);
|
||||
controller.ControllerName = query.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
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;
|
||||
using PoweredSoft.CQRS.DynamicQuery.Discover;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore.Mvc
|
||||
{
|
||||
public class DynamicQueryControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||
{
|
||||
private readonly ServiceProvider serviceProvider;
|
||||
|
||||
public DynamicQueryControllerFeatureProvider(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 != "DynamicQuery")
|
||||
continue;
|
||||
|
||||
if (f is DynamicQueryMeta dynamicQueryMeta)
|
||||
{
|
||||
if (dynamicQueryMeta.ParamsType == null)
|
||||
{
|
||||
var controllerType = typeof(DynamicQueryController<,,>).MakeGenericType(f.QueryType, dynamicQueryMeta.SourceType, dynamicQueryMeta.DestinationType);
|
||||
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||
feature.Controllers.Add(controllerTypeInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
var controllerType = typeof(DynamicQueryController<,,,>).MakeGenericType(f.QueryType, dynamicQueryMeta.SourceType, dynamicQueryMeta.DestinationType, dynamicQueryMeta.ParamsType);
|
||||
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||
feature.Controllers.Add(controllerTypeInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore.Mvc
|
||||
{
|
||||
public class DynamicQueryControllerOptions
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
|
||||
using PoweredSoft.CQRS.DynamicQuery.AspNetCore.Mvc;
|
||||
using PoweredSoft.DynamicQuery.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PoweredSoft.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</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" />
|
||||
<ProjectReference Include="..\PoweredSoft.CQRS.DynamicQuery.Abstractions\PoweredSoft.CQRS.DynamicQuery.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\PoweredSoft.CQRS.DynamicQuery\PoweredSoft.CQRS.DynamicQuery.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
33
PoweredSoft.CQRS.DynamicQuery/Discover/DynamicQueryMeta.cs
Normal file
33
PoweredSoft.CQRS.DynamicQuery/Discover/DynamicQueryMeta.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using PoweredSoft.CQRS.Abstractions.Discovery;
|
||||
using PoweredSoft.CQRS.Discovery;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace PoweredSoft.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 (NameAttribute != null)
|
||||
return NameAttribute.Name;
|
||||
|
||||
var pluralizer = new Pluralize.NET.Pluralizer();
|
||||
return pluralizer.Pluralize(DestinationType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public Type ParamsType { get; internal set; }
|
||||
}
|
||||
}
|
85
PoweredSoft.CQRS.DynamicQuery/DynamicQueryHandler.cs
Normal file
85
PoweredSoft.CQRS.DynamicQuery/DynamicQueryHandler.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
|
||||
using PoweredSoft.DynamicQuery;
|
||||
using PoweredSoft.DynamicQuery.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PoweredSoft.CQRS.DynamicQuery
|
||||
{
|
||||
public class DynamicQueryHandler<TSource, TDestination> :
|
||||
PoweredSoft.CQRS.Abstractions.IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>>
|
||||
where TSource : class
|
||||
where TDestination : class
|
||||
|
||||
{
|
||||
private readonly IQueryHandlerAsync queryHandlerAsync;
|
||||
private readonly IEnumerable<IQueryableProvider<TSource>> queryableProviders;
|
||||
|
||||
public DynamicQueryHandler(IQueryHandlerAsync queryHandlerAsync,
|
||||
IEnumerable<IQueryableProvider<TSource>> queryableProviders)
|
||||
{
|
||||
this.queryHandlerAsync = queryHandlerAsync;
|
||||
this.queryableProviders = queryableProviders;
|
||||
}
|
||||
|
||||
protected virtual Task<IQueryable<TSource>> GetQueryableAsync(IDynamicQuery query, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (this.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()
|
||||
{
|
||||
return Enumerable.Empty<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 = this.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 Task<IQueryable<TSource>> AlterSourceAsync(IQueryable<TSource> source, IDynamicQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(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;
|
||||
}
|
||||
|
||||
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(IDynamicQuery<TSource, TDestination> query, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await ProcessQueryAsync(query, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Pluralize.NET" Version="1.0.2" />
|
||||
<PackageReference Include="PoweredSoft.DynamicQuery" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PoweredSoft.CQRS.DynamicQuery.Abstractions\PoweredSoft.CQRS.DynamicQuery.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\PoweredSoft.CQRS\PoweredSoft.CQRS.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
54
PoweredSoft.CQRS.DynamicQuery/ServiceCollectionExtensions.cs
Normal file
54
PoweredSoft.CQRS.DynamicQuery/ServiceCollectionExtensions.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PoweredSoft.CQRS.Abstractions;
|
||||
using PoweredSoft.CQRS.Abstractions.Discovery;
|
||||
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
|
||||
using PoweredSoft.CQRS.DynamicQuery.Discover;
|
||||
using PoweredSoft.DynamicQuery.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace PoweredSoft.CQRS.DynamicQuery
|
||||
{
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddDynamicQuery<TSource, TDestination>(this IServiceCollection services)
|
||||
where TSource : class
|
||||
where TDestination : class
|
||||
{
|
||||
// add query handler.
|
||||
services.AddTransient<PoweredSoft.CQRS.Abstractions.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);
|
||||
|
||||
services.AddSingleton<IQueryMeta>(queryMeta);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddDynamicQueryWithParams<TSource, TDestination, TParams>(this IServiceCollection services)
|
||||
where TSource : class
|
||||
where TDestination : class
|
||||
{
|
||||
// add query handler.
|
||||
services.AddTransient<PoweredSoft.CQRS.Abstractions.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);
|
||||
|
||||
// params type.
|
||||
queryMeta.ParamsType = typeof(TParams);
|
||||
|
||||
services.AddSingleton<IQueryMeta>(queryMeta);
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
@ -19,6 +19,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.CQRS.DynamicQuery", "PoweredSoft.CQRS.DynamicQuery\PoweredSoft.CQRS.DynamicQuery.csproj", "{A38CE930-191F-417C-B5BE-8CC62DB47513}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.CQRS.DynamicQuery.Abstractions", "PoweredSoft.CQRS.DynamicQuery.Abstractions\PoweredSoft.CQRS.DynamicQuery.Abstractions.csproj", "{60B5E255-77B8-48E0-AE8F-04E8332970F9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.CQRS.DynamicQuery.AspNetCore", "PoweredSoft.CQRS.DynamicQuery.AspNetCore\PoweredSoft.CQRS.DynamicQuery.AspNetCore.csproj", "{0829B99A-0A20-4CAC-A91E-FB67E18444DE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -45,6 +51,18 @@ Global
|
||||
{F15B1E11-8D4C-489E-AFF7-AA144105FE46}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F15B1E11-8D4C-489E-AFF7-AA144105FE46}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F15B1E11-8D4C-489E-AFF7-AA144105FE46}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{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
|
||||
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
Loading…
Reference in New Issue
Block a user