OData support. :)
This commit is contained in:
parent
764f4a7cd6
commit
7a67866dc3
@ -7,6 +7,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation.AspNetCore" Version="9.5.0" />
|
<PackageReference Include="FluentValidation.AspNetCore" Version="9.5.0" />
|
||||||
<PackageReference Include="HotChocolate.AspNetCore" Version="11.0.9" />
|
<PackageReference Include="HotChocolate.AspNetCore" Version="11.0.9" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.5.5" />
|
||||||
<PackageReference Include="PoweredSoft.Data" Version="2.0.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" />
|
||||||
@ -14,6 +15,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PoweredSoft.CQRS.AspNetCore.OData\PoweredSoft.CQRS.AspNetCore.OData.csproj" />
|
||||||
<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.Abstractions\PoweredSoft.CQRS.DynamicQuery.Abstractions.csproj" />
|
||||||
<ProjectReference Include="..\PoweredSoft.CQRS.DynamicQuery.AspNetCore\PoweredSoft.CQRS.DynamicQuery.AspNetCore.csproj" />
|
<ProjectReference Include="..\PoweredSoft.CQRS.DynamicQuery.AspNetCore\PoweredSoft.CQRS.DynamicQuery.AspNetCore.csproj" />
|
||||||
|
@ -24,6 +24,7 @@ using System.Linq;
|
|||||||
using PoweredSoft.CQRS.GraphQL.HotChocolate.DynamicQuery;
|
using PoweredSoft.CQRS.GraphQL.HotChocolate.DynamicQuery;
|
||||||
using PoweredSoft.CQRS.Abstractions.Security;
|
using PoweredSoft.CQRS.Abstractions.Security;
|
||||||
using Demo.Security;
|
using Demo.Security;
|
||||||
|
using Microsoft.AspNet.OData.Extensions;
|
||||||
|
|
||||||
namespace Demo
|
namespace Demo
|
||||||
{
|
{
|
||||||
@ -50,14 +51,15 @@ namespace Demo
|
|||||||
services.AddPoweredSoftDataServices();
|
services.AddPoweredSoftDataServices();
|
||||||
services.AddPoweredSoftDynamicQuery();
|
services.AddPoweredSoftDynamicQuery();
|
||||||
|
|
||||||
services
|
services.AddPoweredSoftCQRS();
|
||||||
.AddPoweredSoftCQRS();
|
services.AddOData();
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddControllers()
|
.AddControllers()
|
||||||
.AddPoweredSoftQueries()
|
.AddPoweredSoftQueries()
|
||||||
.AddPoweredSoftCommands()
|
.AddPoweredSoftCommands()
|
||||||
.AddPoweredSoftDynamicQueries()
|
.AddPoweredSoftDynamicQueries()
|
||||||
|
.AddPoweredSoftODataQueries()
|
||||||
.AddFluentValidation();
|
.AddFluentValidation();
|
||||||
|
|
||||||
services
|
services
|
||||||
@ -128,6 +130,10 @@ namespace Demo
|
|||||||
{
|
{
|
||||||
endpoints.MapControllers();
|
endpoints.MapControllers();
|
||||||
endpoints.MapGraphQL();
|
endpoints.MapGraphQL();
|
||||||
|
|
||||||
|
endpoints.Select().Filter().OrderBy().Count().MaxTop(10);
|
||||||
|
|
||||||
|
endpoints.MapODataRoute("odata", "odata", endpoints.GetPoweredSoftODataEdmModel());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace PoweredSoft.CQRS.AspNetCore.OData.Abstractions
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||||
|
public class QueryOdataControllerIgnoreAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
44
PoweredSoft.CQRS.AspNetCore.OData/EndpointExstensions.cs
Normal file
44
PoweredSoft.CQRS.AspNetCore.OData/EndpointExstensions.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using Microsoft.AspNet.OData.Builder;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.OData.Edm;
|
||||||
|
using PoweredSoft.CQRS.Abstractions.Discovery;
|
||||||
|
using PoweredSoft.CQRS.AspNetCore.OData.Abstractions;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
|
{
|
||||||
|
public static class EndpointExstensions
|
||||||
|
{
|
||||||
|
public static IEdmModel GetPoweredSoftODataEdmModel(this IEndpointRouteBuilder endpoint)
|
||||||
|
{
|
||||||
|
var queryDiscovery = endpoint.ServiceProvider.GetRequiredService<IQueryDiscovery>();
|
||||||
|
|
||||||
|
var odataBuilder = new ODataConventionModelBuilder();
|
||||||
|
odataBuilder.EnableLowerCamelCase();
|
||||||
|
|
||||||
|
foreach(var q in queryDiscovery.GetQueries())
|
||||||
|
{
|
||||||
|
var ignoreAttribute = q.QueryType.GetCustomAttribute<QueryOdataControllerIgnoreAttribute>();
|
||||||
|
if (ignoreAttribute != null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (q.Category != "BasicQuery")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var isQueryable = q.QueryResultType.Namespace == "System.Linq" && q.QueryResultType.Name.Contains("IQueryable");
|
||||||
|
if (!isQueryable)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
var entityType = q.QueryResultType.GetGenericArguments().First();
|
||||||
|
odataBuilder.GetType().GetMethod("EntitySet").MakeGenericMethod(entityType).Invoke(odataBuilder, new object[] {
|
||||||
|
q.LowerCamelCaseName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return odataBuilder.GetEdmModel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
PoweredSoft.CQRS.AspNetCore.OData/MvcBuilderExensions.cs
Normal file
46
PoweredSoft.CQRS.AspNetCore.OData/MvcBuilderExensions.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using PoweredSoft.CQRS.AspNetCore.OData;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
|
{
|
||||||
|
|
||||||
|
public static class MvcBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IMvcBuilder AddPoweredSoftODataQueries(this IMvcBuilder builder, Action<QueryODataControllerOptions> configuration = null)
|
||||||
|
{
|
||||||
|
var options = new QueryODataControllerOptions();
|
||||||
|
configuration?.Invoke(options);
|
||||||
|
var services = builder.Services;
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
builder.AddMvcOptions(o => o.Conventions.Add(new QueryODataControllerConvention(serviceProvider)));
|
||||||
|
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new QueryODataControllerFeatureProvider(serviceProvider)));
|
||||||
|
|
||||||
|
if (options.FixODataSwagger)
|
||||||
|
builder.FixODataSwagger();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IMvcBuilder FixODataSwagger(this IMvcBuilder builder)
|
||||||
|
{
|
||||||
|
builder.AddMvcOptions(options =>
|
||||||
|
{
|
||||||
|
foreach (var outputFormatter in options.OutputFormatters.OfType<OutputFormatter>().Where(x => x.SupportedMediaTypes.Count == 0))
|
||||||
|
{
|
||||||
|
outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var inputFormatter in options.InputFormatters.OfType<InputFormatter>().Where(x => x.SupportedMediaTypes.Count == 0))
|
||||||
|
{
|
||||||
|
inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.5.5" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PoweredSoft.CQRS.Abstractions\PoweredSoft.CQRS.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\PoweredSoft.CQRS.AspNetCore.OData.Abstractions\PoweredSoft.CQRS.AspNetCore.OData.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\PoweredSoft.CQRS.AspNetCore\PoweredSoft.CQRS.AspNetCore.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
23
PoweredSoft.CQRS.AspNetCore.OData/QueryODataController.cs
Normal file
23
PoweredSoft.CQRS.AspNetCore.OData/QueryODataController.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using Microsoft.AspNet.OData;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PoweredSoft.CQRS.Abstractions;
|
||||||
|
using PoweredSoft.CQRS.AspNetCore.Mvc;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PoweredSoft.CQRS.AspNetCore.OData
|
||||||
|
{
|
||||||
|
[Route("api/odata/[controller]")]
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
public class QueryODataController<TQuery, TQueryResult> : ODataController
|
||||||
|
where TQuery : class
|
||||||
|
{
|
||||||
|
[EnableQuery, HttpGet, QueryControllerAuthorization]
|
||||||
|
public async Task<TQueryResult> Get([FromServices]IQueryHandler<TQuery, TQueryResult> queryHandler)
|
||||||
|
{
|
||||||
|
var result = await queryHandler.HandleAsync(null, HttpContext.RequestAborted);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using PoweredSoft.CQRS.Abstractions.Discovery;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
|
{
|
||||||
|
public class QueryODataControllerConvention : IControllerModelConvention
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
|
public QueryODataControllerConvention(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
this.serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(ControllerModel controller)
|
||||||
|
{
|
||||||
|
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("QueryODataController") && controller.ControllerType.Assembly == typeof(QueryODataControllerConvention).Assembly)
|
||||||
|
{
|
||||||
|
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||||
|
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||||
|
var query = queryDiscovery.FindQuery(genericType);
|
||||||
|
controller.ControllerName = $"{query.LowerCamelCaseName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
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.AspNetCore.OData.Abstractions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace PoweredSoft.CQRS.AspNetCore.OData
|
||||||
|
{
|
||||||
|
public class QueryODataControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||||
|
{
|
||||||
|
private readonly ServiceProvider serviceProvider;
|
||||||
|
|
||||||
|
public QueryODataControllerFeatureProvider(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<QueryOdataControllerIgnoreAttribute>();
|
||||||
|
if (ignoreAttribute != null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (f.Category != "BasicQuery")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var isQueryable = f.QueryResultType.Namespace == "System.Linq" && f.QueryResultType.Name.Contains("IQueryable");
|
||||||
|
if (!isQueryable)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var controllerType = typeof(QueryODataController<,>).MakeGenericType(f.QueryType, f.QueryResultType);
|
||||||
|
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
|
feature.Controllers.Add(controllerTypeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
|
{
|
||||||
|
public class QueryODataControllerOptions
|
||||||
|
{
|
||||||
|
public bool FixODataSwagger { get; set; } = true;
|
||||||
|
|
||||||
|
public QueryODataControllerOptions()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.CQRS.GraphQL.Ho
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.CQRS.GraphQL.DynamicQuery", "PoweredSoft.CQRS.GraphQL.DynamicQuery\PoweredSoft.CQRS.GraphQL.DynamicQuery.csproj", "{34B27880-A5D5-47EA-A5FA-86E04E0F7A21}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.CQRS.GraphQL.DynamicQuery", "PoweredSoft.CQRS.GraphQL.DynamicQuery\PoweredSoft.CQRS.GraphQL.DynamicQuery.csproj", "{34B27880-A5D5-47EA-A5FA-86E04E0F7A21}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.CQRS.AspNetCore.OData", "PoweredSoft.CQRS.AspNetCore.OData\PoweredSoft.CQRS.AspNetCore.OData.csproj", "{04459C2D-B02F-4FF0-8D66-73042F27C7CC}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.CQRS.AspNetCore.OData.Abstractions", "PoweredSoft.CQRS.AspNetCore.OData.Abstractions\PoweredSoft.CQRS.AspNetCore.OData.Abstractions.csproj", "{9B65B727-C088-4562-A607-8BD5B5EFF289}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -93,6 +97,14 @@ Global
|
|||||||
{34B27880-A5D5-47EA-A5FA-86E04E0F7A21}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{34B27880-A5D5-47EA-A5FA-86E04E0F7A21}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{34B27880-A5D5-47EA-A5FA-86E04E0F7A21}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{34B27880-A5D5-47EA-A5FA-86E04E0F7A21}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{34B27880-A5D5-47EA-A5FA-86E04E0F7A21}.Release|Any CPU.Build.0 = Release|Any CPU
|
{34B27880-A5D5-47EA-A5FA-86E04E0F7A21}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{04459C2D-B02F-4FF0-8D66-73042F27C7CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{04459C2D-B02F-4FF0-8D66-73042F27C7CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{04459C2D-B02F-4FF0-8D66-73042F27C7CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{04459C2D-B02F-4FF0-8D66-73042F27C7CC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9B65B727-C088-4562-A607-8BD5B5EFF289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9B65B727-C088-4562-A607-8BD5B5EFF289}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9B65B727-C088-4562-A607-8BD5B5EFF289}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9B65B727-C088-4562-A607-8BD5B5EFF289}.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