Modernize ref-data querying, fix alias resolution, add coreex-graphql skill - #174
Conversation
ReferenceDataOrchestrator now supports QueryAsync<TRef> with dynamic LINQ-based filtering, ordering, and paging via QueryArgs and PagingArgs, replacing static codes/text/inactive filters. Controller endpoints and generated templates use the new querying model, removing DataMap and legacy GetWithFilterAsync. GraphQLLiteOptions and WebApiOptionsBase updated for friendly names and query support. Tests updated for new syntax and behaviors. New files define query configuration and implementation.
- Add non-generic QueryAsync(Type, ...) to ReferenceDataOrchestrator/IReferenceDataQuery for runtime ref-data queries. - Refactor ReferenceDataQuery for per-type QueryArgsConfig selection; fallback to default config. - Auto-register ReferenceDataQuery.Default in AddReferenceDataOrchestrator for OData-style filtering/paging. - Update API controller/host setup docs to use new QueryAsync methods. - Document AddReferenceDataQueries DI extension for GraphQL bulk registration. - Update GraphQLEngine/GraphQLLiteOptions to use non-generic QueryAsync for ref-data roots. - Enforce single alternate name per provider for ref-data types. - Add tests for QueryAsync(Type, ...), per-type config, and duplicate name enforcement. - Update READMEs for new query, DI, and GraphQL features.
…skill - ReferenceDataOrchestrator: fix GetNamedAsync silently dropping a type's primary name once it had an alias by adding a reverse _typeToName map; ReplaceNames now resolves input via _nameToType (accepts both primary and alias names) and normalizes output via _typeToName. - Add matching ReferenceDataOrchestratorTests coverage. - Update CoreEx.Data.GraphQL AGENTS.md/README to reflect that AddReferenceDataQueries exposes every ref-data type known to the orchestrator, not just types with an AlternateNames entry. - Add new coreex-graphql L1 skill (SKILL.md + references/workflow.md) and matching coreex-graphql.prompt.md, covering first-time AddCoreExGraphQLLite/MapCoreExGraphQLLite wiring, entity query/get root registration, bulk reference-data exposure, and recording enablement in a host's AGENTS.md. - Amend coreex-api skill to offer a matching GraphQL root after scaffolding a query endpoint on a host with GraphQL already enabled. - Point coreex-host-setup/coreex-api-controllers instructions at the new skill instead of prose-only guidance. - Register coreex-graphql in the copilot-instructions.md and coreex-ai-workflows.md skill catalogs, and wire it into CoreEx.Template.csproj's CopyTemplateAiContext packing target so dotnet new coreex-ai ships it. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR modernizes reference-data querying to use the same QueryArgsConfig/dynamic-LINQ pipeline as the rest of CoreEx, fixes reference-data alias/primary-name resolution in ReferenceDataOrchestrator.GetNamedAsync, expands GraphQL-lite to bulk-expose reference data, and ships a new coreex-graphql AI skill (prompt + packaging) to standardize GraphQL-lite enablement/wiring in API hosts.
Changes:
- Replace bespoke ref-data
codes/textPatternfiltering withReferenceDataOrchestrator.QueryAsync(...)backed by a pluggableIReferenceDataQuery(ReferenceDataQuery.Default+ReferenceDataQueryArgsConfig.Default). - Fix alternate-name handling so primary type names remain resolvable while results normalize to the canonical external name; add/extend unit + sample tests for the new behavior.
- Add GraphQL-lite bulk reference-data root registration (
AddReferenceDataQueries) and author a newcoreex-graphqlskill/prompt, then wire it into the AI catalogs and template packing.
Reviewed changes
Copilot reviewed 45 out of 45 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/CoreEx.RefData.Test.Unit/ReferenceDataOrchestratorTests.cs | Updates/refactors tests to cover new QueryAsync path, per-type config selector, and alias/primary naming behavior. |
| tests/CoreEx.Data.GraphQL.Test.Unit/GraphQLLiteOptionsTests.cs | Adds coverage for bulk ref-data GraphQL root registration and exclusions. |
| tests/CoreEx.AspNetCore.Test.Unit/ReferenceDataApi_TestsBase.cs | Updates ref-data API tests to use $filter-style query strings via WithQuery(...)/include-inactive semantics. |
| tests/CoreEx.AspNetCore.Test.Api/Services/ReferenceDataService.cs | Cleans up test provider implementation imports. |
| tests/CoreEx.AspNetCore.Test.Api/Services/PersonService2.cs | Aligns query service signature to accept nullable PagingArgs. |
| tests/CoreEx.AspNetCore.Test.Api/Services/PersonService.cs | Aligns query service signature to accept nullable PagingArgs. |
| tests/CoreEx.AspNetCore.Test.Api/Program.cs | Updates minimal API ref-data endpoint to use QueryAsync and ro.QueryArgs/ro.PagingArgs. |
| tests/CoreEx.AspNetCore.Test.Api/Controllers/ReferenceDataController.cs | Updates MVC ref-data controller to use QueryAsync and ro.QueryArgs/ro.PagingArgs. |
| src/CoreEx/RefData/ReferenceDataOrchestrator.cs | Implements alias/primary normalization maps, adds RegisterQuery + QueryAsync APIs, and fixes GetNamedAsync canonical naming. |
| src/CoreEx/RefData/README.md | Documents new QueryAsync + IReferenceDataQuery strategy capabilities and naming semantics. |
| src/CoreEx/RefData/IReferenceDataProvider.cs | Adds AlternateNames support for providers. |
| src/CoreEx/RefData/Abstractions/IReferenceDataQuery.cs | Introduces the query strategy interface used by ReferenceDataOrchestrator.QueryAsync. |
| src/CoreEx/Extensions.HttpRequestMessage.cs | Adds request helpers for include-text / include-inactive query flags. |
| src/CoreEx.Template/CoreEx.Template.csproj | Ensures the new coreex-graphql prompt/skill content is packaged by the template. |
| src/CoreEx.RefData/ReferenceDataQueryArgsConfig.cs | Adds default ref-data query config (filter/orderby) for the new query pipeline. |
| src/CoreEx.RefData/README.md | Documents default ref-data querying behavior and how to customize per type. |
| src/CoreEx.RefData/GlobalUsing.cs | Adds CoreEx.Data querying dependencies needed by the new ref-data query implementation. |
| src/CoreEx.RefData/CoreExReferenceDataExtensions.DependencyInjection.cs | Auto-registers ReferenceDataQuery.Default when an orchestrator factory doesn’t supply one. |
| src/CoreEx.RefData/CoreEx.RefData.csproj | Adds a project reference to CoreEx.Data to support querying. |
| src/CoreEx.RefData/AGENTS.md | Documents ReferenceDataQuery.Default auto-registration and per-type config customization. |
| src/CoreEx.RefData/Abstractions/ReferenceDataQuery.cs | Implements the default dynamic-LINQ backed ref-data query strategy. |
| src/CoreEx.Data.GraphQL/README.md | Updates docs to reflect bulk ref-data exposure behavior and naming rules. |
| src/CoreEx.Data.GraphQL/GraphQLLiteOptions.cs | Adds AddQuery(Type, ...) and AddReferenceDataQueries(...) bulk ref-data root registration. |
| src/CoreEx.Data.GraphQL/GraphQLEngine.cs | Routes schema execution and query/item root execution through the invoker for tracing/consistency. |
| src/CoreEx.Data.GraphQL/GlobalUsing.cs | Adds System.Linq.Expressions (currently needs re-sorting). |
| src/CoreEx.Data.GraphQL/AGENTS.md | Documents bulk ref-data root registration and naming rules for GraphQL-lite. |
| src/CoreEx.CodeGen/RefData/Templates/Service_cs.hbs | Generates provider AlternateNames to support stable external naming. |
| src/CoreEx.CodeGen/RefData/Templates/Controller_cs.hbs | Updates generated ref-data controllers to use QueryAsync and includes query/paging metadata. |
| src/CoreEx.AspNetCore/Abstractions/WebApiOptionsBase.cs | Makes PagingArgs nullable when not specified; retains QueryArgs parsing. |
| samples/tests/Contoso.Products.Test.Api/HostTests.ReferenceData.cs | Expands sample tests around multi-name query normalization/deduping. |
| samples/src/Contoso.Shopping.Application/ReferenceDataService.g.cs | Updates generated sample ref-data service to emit alternate-name mappings. |
| samples/src/Contoso.Shopping.Api/Controllers/ReferenceDataController.g.cs | Updates generated sample ref-data controllers to QueryAsync and removes old mapping/filter patterns. |
| samples/src/Contoso.Products.Application/ReferenceDataService.g.cs | Updates generated sample ref-data service to emit alternate-name mappings. |
| samples/src/Contoso.Products.Api/Program.cs | Adds bulk ref-data GraphQL roots via AddReferenceDataQueries. |
| samples/src/Contoso.Products.Api/Controllers/ReferenceDataController.g.cs | Updates generated sample ref-data controllers to QueryAsync. |
| samples/src/Contoso.Orders.Application/ReferenceDataService.cs | Adds AlternateNames to the Orders ref-data provider. |
| samples/src/Contoso.Orders.Api/Controllers/ReferenceDataController.cs | Updates Orders ref-data endpoint to QueryAsync. |
| .github/skills/coreex-graphql/SKILL.md | Introduces the new coreex-graphql skill documentation and usage rules. |
| .github/skills/coreex-graphql/references/workflow.md | Adds detailed workflow steps and guardrails for GraphQL-lite enablement. |
| .github/skills/coreex-api/SKILL.md | Updates coreex-api skill to optionally hand off to coreex-graphql when GraphQL is already enabled. |
| .github/prompts/coreex-graphql.prompt.md | Adds the Copilot prompt wrapper for the new skill. |
| .github/instructions/coreex-host-setup.instructions.md | Updates host setup guidance to reference the new coreex-graphql skill and ref-data querying defaults. |
| .github/instructions/coreex-api-controllers.instructions.md | Updates controller guidance for ref-data endpoints to use QueryAsync + [Query]/[Paging] metadata. |
| .github/coreex-ai-workflows.md | Registers coreex-graphql in the L1 skills catalog count/list. |
| .github/copilot-instructions.md | Registers coreex-graphql in the repo’s Copilot instructions catalog. |
…sync, use params Type[] for excludeTypes, fix using-order, add Query/Paging OpenAPI metadata to ref-data query endpoints Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 45 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
tests/CoreEx.Data.GraphQL.Test.Unit/GraphQLLiteOptionsTests.cs:93
CreateOrchestratorServiceProviderdisposesproviderServiceProvider(using var) even though the createdReferenceDataOrchestratorretains thatIServiceProviderfor later scope creation. This leaves the orchestrator backed by a disposed provider, which can causeObjectDisposedExceptionif any future test (or refactor) calls into orchestrator methods that requireServiceProvider(e.g.GetByType*/QueryAsync).
src/CoreEx/RefData/ReferenceDataOrchestrator.cs:128- When an alternate name equals an already-registered name for the same type (e.g. the provider mistakenly sets
AlternateNamesto("DummyRefData", typeof(DummyRefData))), theprimary is not nullbranch hitscontinuewithout populating_nameMappings/_typeToName. Because the later primary-name mapping loop skips types that appear inAlternateNames, this leaves the type with no definitive external-name mapping, soGetAlternateNameMappings()omits it andReplaceNamescan’t normalize it.
src/CoreEx.CodeGen/RefData/Templates/Controller_cs.hbs:21 - The generated ref-data controller actions drop cancellation propagation: they don’t accept a
CancellationTokenand don’t passcancellationToken:into_webApi.GetAsync(...). This conflicts with the repo’s controller convention of taking a trailing token and flowing it through to the WebApi helper and downstream calls.
[Query(supportsOrderBy: true), Paging(supportsCount: true)]
public Task<IActionResult> Get{{Plural}}Async() => _webApi.GetAsync(Request, (ro, ct) => ReferenceDataOrchestrator.Current.QueryAsync<{{Name}}>(ro.QueryArgs, ro.PagingArgs, ct));
src/CoreEx.CodeGen/RefData/Templates/Controller_cs.hbs:26
GetNamedAsyncin the template also omits the standard trailingCancellationTokenand doesn’t pass it to_webApi.GetAsync(...), so generated controllers won’t honor request cancellation.
[HttpGet]
[ProducesResponseType(typeof(ReferenceDataMultiDictionary), 200)]
public Task<IActionResult> GetNamedAsync([FromQuery] string[] name) => _webApi.GetAsync(Request, (ro, ct) => ReferenceDataOrchestrator.Current.GetNamedAsync(name, ro.IsIncludeInactive, ct));
ReferenceDataController methods now accept a CancellationToken and pass it to _webApi.GetAsync, enabling request cancellation. Updated Controller_cs.hbs template to generate these signatures for future codegen consistency.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (3)
tests/CoreEx.RefData.Test.Unit/ReferenceDataOrchestratorTests.cs:361
- This test claims it uses a per-type config that "only allows filtering on Code", but
new ReferenceDataQueryArgsConfig()also enables filtering onText. As written, the assertion would still pass even if the per-type selector were ignored (because the default config behaves the same). Use a truly code-onlyQueryArgsConfigand assert that atextfilter is rejected to prove the selector is applied.
src/CoreEx.CodeGen/RefData/Templates/Controller_cs.hbs:22 - The codegen controller template takes a required
CancellationTokenparameter. Across CoreEx controllers the convention isCancellationToken cancellationToken = defaultso callers aren’t forced to supply one and the signature matches the rest of the repo’s controller examples.
[Query(supportsOrderBy: true), Paging(supportsCount: true)]
public Task<IActionResult> Get{{Plural}}Async(CancellationToken cancellationToken)
=> _webApi.GetAsync(Request, (ro, ct) => ReferenceDataOrchestrator.Current.QueryAsync<{{Name}}>(ro.QueryArgs, ro.PagingArgs, ct), cancellationToken: cancellationToken);
src/CoreEx.CodeGen/RefData/Templates/Controller_cs.hbs:28
- The codegen controller template’s GetNamedAsync signature should also follow the repo controller convention of
CancellationToken cancellationToken = defaultas the trailing optional parameter.
[HttpGet]
[ProducesResponseType(typeof(ReferenceDataMultiDictionary), 200)]
public Task<IActionResult> GetNamedAsync([FromQuery] string[] name, CancellationToken cancellationToken)
=> _webApi.GetAsync(Request, (ro, ct) => ReferenceDataOrchestrator.Current.GetNamedAsync(name, ro.IsIncludeInactive, ct), cancellationToken: cancellationToken);
Summary
This branch moves reference data off its bespoke
codes/textPatternfilter onto the sameQueryArgsConfig/dynamic-LINQ pipeline used elsewhere, adds a pluggableIReferenceDataQuery, bulk-exposes reference data through GraphQL, and fixes two bugs found during review — plus adds a new AI-enablement skill forCoreEx.Data.GraphQL.Bug fixes
ReferenceDataOrchestrator.GetNamedAsyncsilently dropped a type's primary name once it had an alias. Fixed by adding a reverse_typeToNamemap;ReplaceNamesnow resolves input via_nameToType(accepts both primary and alias names) and normalizes output via_typeToName. Added matchingReferenceDataOrchestratorTestscoverage.CoreEx.Data.GraphQLAGENTS.md/README.md—AddReferenceDataQueriesexposes every ref-data type known to the orchestrator, not just types with anAlternateNamesentry.New:
coreex-graphqlAI skill.github/skills/coreex-graphql/SKILL.md+references/workflow.md) and matching.github/prompts/coreex-graphql.prompt.md, covering first-timeAddCoreExGraphQLLite/MapCoreExGraphQLLitewiring, entity query/get root registration, bulk reference-data exposure, and recording enablement in a host'sAGENTS.md.coreex-apiskill to offer a matching GraphQL root after scaffolding a query endpoint, when the host already has GraphQL enabled.coreex-host-setup/coreex-api-controllersinstructions at the new skill instead of prose-only guidance.copilot-instructions.md/coreex-ai-workflows.mdcatalogs and wired it intoCoreEx.Template.csproj'sCopyTemplateAiContextpacking target sodotnet new coreex-aiships it.Validation
dotnet build src/CoreEx.Template/CoreEx.Template.csproj -c Release— succeeds; verified the packed.nupkgcontains the newcoreex-graphqlprompt/skill files undercontent/CoreEx.Ai/.github/....dotnet build CoreEx.sln -c Release— 0 errors, 0 warnings.Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com