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>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation.AspNetCore" Version="9.5.0" />
|
<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.Swagger" Version="5.6.3" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" />
|
||||||
@ -13,6 +14,9 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PoweredSoft.CQRS.AspNetCore\PoweredSoft.CQRS.AspNetCore.csproj" />
|
<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" />
|
<ProjectReference Include="..\PoweredSoft.CQRS\PoweredSoft.CQRS.csproj" />
|
||||||
</ItemGroup>
|
</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.Commands;
|
||||||
|
using Demo.DynamicQueries;
|
||||||
using Demo.Queries;
|
using Demo.Queries;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using FluentValidation.AspNetCore;
|
using FluentValidation.AspNetCore;
|
||||||
@ -13,6 +15,12 @@ using Microsoft.Extensions.Logging;
|
|||||||
using PoweredSoft.CQRS;
|
using PoweredSoft.CQRS;
|
||||||
using PoweredSoft.CQRS.Abstractions;
|
using PoweredSoft.CQRS.Abstractions;
|
||||||
using PoweredSoft.CQRS.AspNetCore.Mvc;
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -33,18 +41,30 @@ namespace Demo
|
|||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
AddQueries(services);
|
AddQueries(services);
|
||||||
|
AddDynamicQueries(services);
|
||||||
AddCommands(services);
|
AddCommands(services);
|
||||||
|
|
||||||
|
services.AddTransient<IAsyncQueryableHandlerService, InMemoryQueryableHandler>();
|
||||||
|
services.AddPoweredSoftDataServices();
|
||||||
|
services.AddPoweredSoftDynamicQuery();
|
||||||
|
|
||||||
services.AddPoweredSoftCQRS();
|
services.AddPoweredSoftCQRS();
|
||||||
services
|
services
|
||||||
.AddControllers()
|
.AddControllers()
|
||||||
.AddPoweredSoftQueries()
|
.AddPoweredSoftQueries()
|
||||||
.AddPoweredSoftCommands()
|
.AddPoweredSoftCommands()
|
||||||
|
.AddPoweredSoftDynamicQueries()
|
||||||
.AddFluentValidation();
|
.AddFluentValidation();
|
||||||
|
|
||||||
services.AddSwaggerGen();
|
services.AddSwaggerGen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddDynamicQueries(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddTransient<IQueryableProvider<Contact>, ContactQueryableProvider>();
|
||||||
|
services.AddDynamicQuery<Contact, Contact>();
|
||||||
|
}
|
||||||
|
|
||||||
private void AddCommands(IServiceCollection services)
|
private void AddCommands(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler>();
|
services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler>();
|
||||||
|
@ -11,5 +11,6 @@ namespace PoweredSoft.CQRS.Abstractions.Discovery
|
|||||||
Type QueryType { get; }
|
Type QueryType { get; }
|
||||||
Type ServiceType { get; }
|
Type ServiceType { get; }
|
||||||
Type QueryResultType { get; }
|
Type QueryResultType { get; }
|
||||||
|
string Category { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,5 +27,6 @@ namespace PoweredSoft.CQRS.Abstractions.Discovery
|
|||||||
public virtual Type QueryType { get; }
|
public virtual Type QueryType { get; }
|
||||||
public virtual Type ServiceType { get; }
|
public virtual Type ServiceType { get; }
|
||||||
public virtual Type QueryResultType { get; }
|
public virtual Type QueryResultType { get; }
|
||||||
|
public virtual string Category => "BasicQuery";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,23 +5,23 @@ using System;
|
|||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
{
|
{
|
||||||
public class QueryControllerConvention : IControllerModelConvention
|
public class CommandControllerConvention : IControllerModelConvention
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
public QueryControllerConvention(IServiceProvider serviceProvider)
|
public CommandControllerConvention(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply(ControllerModel controller)
|
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 genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||||
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
|
||||||
var query = queryDiscovery.FindQuery(genericType);
|
var command = commandDiscovery.FindCommand(genericType);
|
||||||
controller.ControllerName = query.Name;
|
controller.ControllerName = command.Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,23 +5,23 @@ using System;
|
|||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
{
|
{
|
||||||
public class CommandControllerConvention : IControllerModelConvention
|
public class QueryControllerConvention : IControllerModelConvention
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
public CommandControllerConvention(IServiceProvider serviceProvider)
|
public QueryControllerConvention(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply(ControllerModel controller)
|
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 genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||||
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
|
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||||
var command = commandDiscovery.FindCommand(genericType);
|
var query = queryDiscovery.FindQuery(genericType);
|
||||||
controller.ControllerName = command.Name;
|
controller.ControllerName = query.Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,9 @@ namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|||||||
if (ignoreAttribute != null)
|
if (ignoreAttribute != null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (f.Category != "BasicQuery")
|
||||||
|
continue;
|
||||||
|
|
||||||
var controllerType = typeof(QueryController<,>).MakeGenericType(f.QueryType, f.QueryResultType);
|
var controllerType = typeof(QueryController<,>).MakeGenericType(f.QueryType, f.QueryResultType);
|
||||||
var controllerTypeInfo = controllerType.GetTypeInfo();
|
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
feature.Controllers.Add(controllerTypeInfo);
|
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
|
README.md = README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{F15B1E11-8D4C-489E-AFF7-AA144105FE46}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
Loading…
Reference in New Issue
Block a user