diff --git a/.github/workflows/PullRequest-CI.yml b/.github/workflows/PullRequest-CI.yml
index cd8dfb2..c3c8441 100644
--- a/.github/workflows/PullRequest-CI.yml
+++ b/.github/workflows/PullRequest-CI.yml
@@ -10,7 +10,7 @@ jobs:
Run-Lint:
runs-on: ubuntu-latest
env:
- github-token: '${{ secrets.GITHUB_TOKEN }}'
+ github-token: '${{ secrets.GH_Packages }}'
steps:
- name: Step-01 Checkout code
uses: actions/checkout@v3
@@ -52,7 +52,7 @@ jobs:
- name: Step-04 Install .NET
uses: actions/setup-dotnet@v3
with:
- dotnet-version: 6.0.x
+ dotnet-version: 9.0.x
- name: Step-05 Restore dependencies
run: dotnet restore
@@ -85,7 +85,7 @@ jobs:
outputs:
semVersion: ${{ needs.Build-Release.outputs.semVersion }}
env:
- github-token: '${{ secrets.GITHUB_TOKEN }}'
+ github-token: '${{ secrets.GH_Packages }}'
nuget-token: '${{ secrets.NUGET_API_KEY }}'
working-directory: /home/runner/work/Schemio/Schemio
steps:
@@ -100,16 +100,16 @@ jobs:
- name: Step-03 Publish to Github Packages
run: find -name "*.nupkg" -print -exec gpr push -k ${{env.github-token}} {} \;
-
- - name: Step-03 Release to Nuget Org
- if: ${{ startsWith(github.head_ref, 'release/')}}
- run: dotnet nuget push ${{env.working-directory}}/src/Schemio/bin/Release/*.nupkg --skip-duplicate --api-key ${{ env.nuget-token }} --source https://api.nuget.org/v3/index.json
Release:
name: Release to Nuget
needs: [Package]
if: ${{ startsWith(github.head_ref, 'release/')}}
runs-on: ubuntu-latest
+ env:
+ github-token: '${{ secrets.GH_Packages }}'
+ nuget-token: '${{ secrets.NUGET_API_KEY }}'
+ working-directory: /home/runner/work/Schemio/Schemio
steps:
- uses: actions/checkout@v2
@@ -145,10 +145,10 @@ jobs:
# TAG_FORMAT: v*
# API key to authenticate with NuGet server, or a token, issued for GITHUB_USER if you use GPR
- # NUGET_KEY: ${{secrets.NUGET_API_KEY}}
+ NUGET_KEY: ${{secrets.NUGET_API_KEY}}
# NuGet server uri hosting the packages, defaults to https://api.nuget.org
- # NUGET_SOURCE: https://api.nuget.org
+ NUGET_SOURCE: https://api.nuget.org/v3/index.json
# Flag to toggle pushing symbols along with nuget package to the server, disabled by default
# INCLUDE_SYMBOLS: false
diff --git a/.github/workflows/PullRequest-CodeQL.yml b/.github/workflows/PullRequest-CodeQL.yml
index c1b075e..741c609 100644
--- a/.github/workflows/PullRequest-CodeQL.yml
+++ b/.github/workflows/PullRequest-CodeQL.yml
@@ -73,4 +73,4 @@ jobs:
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
- category: "/language:${{matrix.language}}"
\ No newline at end of file
+ category: "/language:${{matrix.language}}"
diff --git a/GitVersion.yml b/GitVersion.yml
index 89b0a62..280505f 100644
--- a/GitVersion.yml
+++ b/GitVersion.yml
@@ -1,4 +1,4 @@
-next-version: 1.0.0
+next-version: 2.0.0
tag-prefix: '[vV]'
mode: ContinuousDeployment
branches:
diff --git a/Images/Schemio-Control-Flow.png b/Images/Schemio-Control-Flow.png
new file mode 100644
index 0000000..0f3c7db
Binary files /dev/null and b/Images/Schemio-Control-Flow.png differ
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..daf3b65
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Code Shayk
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 2d23968..55f3cc9 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
-# Schemio v1.0
+# Schemio v2.0
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/CodeShayk/Schemio/blob/master/LICENSE.md)
[![Master-Build](https://github.com/CodeShayk/Schemio/actions/workflows/Build-Master.yml/badge.svg)](https://github.com/CodeShayk/Schemio/actions/workflows/Build-Master.yml)
[![GitHub Release](https://img.shields.io/github/v/release/CodeShayk/Schemio?logo=github&sort=semver)](https://github.com/CodeShayk/Schemio/releases/latest)
[![Master-CodeQL](https://github.com/CodeShayk/Schemio/actions/workflows/Master-CodeQL.yml/badge.svg)](https://github.com/CodeShayk/Schemio/actions/workflows/Master-CodeQL.yml)
-[![.Net 8.0](https://img.shields.io/badge/.Net-8.0-blue)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
+[![.Net 9.0](https://img.shields.io/badge/.Net-9.0-blue)](https://dotnet.microsoft.com/en-us/download/dotnet/9.0)
--
#### Nuget Packages
| Package | Latest | Details |
@@ -11,12 +11,15 @@
| Schemio.Core|[![NuGet version](https://badge.fury.io/nu/Schemio.Core.svg)](https://badge.fury.io/nu/Schemio.Core) | Provides `core` functionality to configure nested queries and transformers. With ability to map schema paths (XPath/JSONPath) to entity's object graph. `No QueryEngine` provided and requires implementing IQueryEngine to execute IQuery instances. |
| Schemio.SQL|[![NuGet version](https://badge.fury.io/nu/Schemio.SQL.svg)](https://badge.fury.io/nu/Schemio.SQL)| Provides schemio with query engine using `Dapper` to execute SQL queries. |
| Schemio.EntityFramework|[![NuGet version](https://badge.fury.io/nu/Schemio.EntityFramework.svg)](https://badge.fury.io/nu/Schemio.EntityFramework)| Provides schemio with `Entity Framework` query engine to execute queries using DbContext. |
+| Schemio.API|[![NuGet version](https://badge.fury.io/nu/Schemio.Api.svg)](https://badge.fury.io/nu/Schemio.Api)| Provides schemio with `Web Api` query engine to execute apis using HttpClient. |
## Concept
### What is Schemio?
-`Schemio` is a data aggregation framework that
-- allows fetching `aggregated data` from `heterogeneous` data storages. You could combine queries targetting different data platforms (example. SQL, API, Cache) to return an aggregated data entity.
-- allows `conditionally` fetching only `parts` of the data entity. You could retrieve an aggregated data entity with selective sections of its `object graph` populated depending on the context passed with the request.
+`Schemio` is a data aggregation framework using queries that can target different data platforms.
+
+Key benefits:
+- allows fetching `aggregated` data from `heterogeneous` data storages. You could combine queries targetting different data platforms (example. `SQL`, `API`, `Cache`) to return an aggregated data `entity`.
+- allows `selectively` fetching only `parts` of the aggregated data entity. You could retrieve an aggregated data entity with only `sections` of its `object graph` populated with data depending upon the `schema paths` passed with the request.
### When to use Schemio?
Schemio is perfect fit for many use cases. Few examples that require the service tier to dynamically fetch aggregated data with high performance, availability and scalability are
@@ -28,7 +31,7 @@ Schemio is perfect fit for many use cases. Few examples that require the service
## Getting Started?
### i. Installation
-Install the latest nuget package as appropriate for Core, SQL using Dapper or EntityFramework.
+Install the latest nuget package as appropriate for `Core`, `Web API`, `SQL` using `Dapper` or `EntityFramework` using commands below.
`Scemio.Core` - for installing schemio for `bespoke` implementation of query engine.
```
@@ -36,13 +39,16 @@ NuGet\Install-Package Schemio.Core
```
`Schemio.SQL` - for installing schemio for SQL with `Dapper` engine.
```
-NuGet\Install-Package Schemio.SQL
+NuGet\Install-Package Schemio.SQL
```
`Schemio.EntityFramework` - for installing schemio for SQL with `EntityFramework` engine.
```
NuGet\Install-Package Schemio.EntityFramework
```
-
+`Schemio.API` - for installing schemio for Web API with `HttpClient` engine.
+```
+NuGet\Install-Package Schemio.API
+```
### ii. Developer Guide
Please see [Developer Guide](https://codeshayk.github.io/Schemio/) for details on how to implement schemio in your project.
@@ -56,11 +62,11 @@ If you are having problems, please let me know by [raising a new issue](https://
This project is licensed with the [MIT license](LICENSE).
## Version History
-The main branch is now on .NET 8.0. The following previous versions are available:
+The main branch is now on .NET 9.0. The following previous versions are available:
| Version | Release Notes | Developer Guide |
| -------- | --------|--------|
| [`v1.0.0`](https://github.com/CodeShayk/Schemio/tree/v1.0.0) | [Notes](https://github.com/CodeShayk/Schemio/releases/tag/v1.0.0) | [Guide](https://github.com/CodeShayk/Schemio/blob/v1.0.0/index.md) |
-| [`Pre-Release v2.0.0`](https://github.com/CodeShayk/Schemio/tree/v2.0.0) | [Notes](https://github.com/CodeShayk/Schemio/releases/tag/v2.0.0) | [Guide](https://github.com/CodeShayk/Schemio/blob/v2.0.0/index.md) |
+| [`v2.0.0`](https://github.com/CodeShayk/Schemio/tree/v2.0.0) | [Notes](https://github.com/CodeShayk/Schemio/releases/tag/v2.0.0) | [Guide](https://github.com/CodeShayk/Schemio/blob/v2.0.0/index.md) |
## Credits
Thank you for reading. Please fork, explore, contribute and report. Happy Coding !! :)
diff --git a/Schemio.sln b/Schemio.sln
index 837bcd6..75e0047 100644
--- a/Schemio.sln
+++ b/Schemio.sln
@@ -5,22 +5,17 @@ VisualStudioVersion = 17.5.33516.290
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F41DA3D8-A0E9-4A05-8A35-78313C0F5804}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio", "src\Schemio\Schemio.Core.csproj", "{6F017146-B95A-4081-9CC0-B0245F78D72B}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{07BAE427-96CF-4F9B-80A9-48CFB0A89CF3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution", "solution", "{AF995FEF-BB94-48D0-B02B-6671DA73056B}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
+ index.md = index.md
LICENSE.md = LICENSE.md
README.md = README.md
EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.SQL", "src\Schemio.SQL\Schemio.SQL.csproj", "{1A0CB973-23C9-4A17-905E-59510CD18932}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.EntityFramework", "src\Schemio.EntityFramework\Schemio.EntityFramework.csproj", "{6B92CC17-B7DB-446F-BF2F-A93696D48B5D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.Tests", "tests\Schemio.Tests\Schemio.Tests.csproj", "{B09236CB-BBD2-4DCF-A698-74CCCAB29FFB}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.Core.Tests", "tests\Schemio.Core.Tests\Schemio.Core.Tests.csproj", "{B09236CB-BBD2-4DCF-A698-74CCCAB29FFB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.SQL.Tests", "tests\Schemio.SQL.Tests\Schemio.SQL.Tests.csproj", "{1E319404-8EF0-40A1-A9D7-A404A71A98C4}"
EndProject
@@ -35,24 +30,22 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{39FD80
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.EntityFramework.Tests", "tests\Schemio.EntityFramework.Tests\Schemio.EntityFramework.Tests.csproj", "{FE039675-00BE-4DC5-945F-14E7625170D0}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.Core", "src\Schemio.Core\Schemio.Core.csproj", "{4F9B6FE3-CAC4-4594-84AD-98D1ECDE180C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.EntityFramework", "src\Schemio.EntityFramework\Schemio.EntityFramework.csproj", "{E8F8C13E-2E05-4092-84FD-B7B7B12DABBB}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.SQL", "src\Schemio.SQL\Schemio.SQL.csproj", "{52986844-698F-486B-AEC9-846AAF50CF46}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.API", "src\Schemio.API\Schemio.API.csproj", "{0C1A05D2-653D-4F88-B397-BA53E0BA7281}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schemio.API.Tests", "tests\Schemio.API.Tests\Schemio.API.Tests.csproj", "{D0EE40C1-87EF-413D-A046-DC37D58BC344}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {6F017146-B95A-4081-9CC0-B0245F78D72B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6F017146-B95A-4081-9CC0-B0245F78D72B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6F017146-B95A-4081-9CC0-B0245F78D72B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6F017146-B95A-4081-9CC0-B0245F78D72B}.Release|Any CPU.Build.0 = Release|Any CPU
- {1A0CB973-23C9-4A17-905E-59510CD18932}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1A0CB973-23C9-4A17-905E-59510CD18932}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1A0CB973-23C9-4A17-905E-59510CD18932}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1A0CB973-23C9-4A17-905E-59510CD18932}.Release|Any CPU.Build.0 = Release|Any CPU
- {6B92CC17-B7DB-446F-BF2F-A93696D48B5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6B92CC17-B7DB-446F-BF2F-A93696D48B5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6B92CC17-B7DB-446F-BF2F-A93696D48B5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6B92CC17-B7DB-446F-BF2F-A93696D48B5D}.Release|Any CPU.Build.0 = Release|Any CPU
{B09236CB-BBD2-4DCF-A698-74CCCAB29FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B09236CB-BBD2-4DCF-A698-74CCCAB29FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B09236CB-BBD2-4DCF-A698-74CCCAB29FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -65,18 +58,40 @@ Global
{FE039675-00BE-4DC5-945F-14E7625170D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE039675-00BE-4DC5-945F-14E7625170D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE039675-00BE-4DC5-945F-14E7625170D0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4F9B6FE3-CAC4-4594-84AD-98D1ECDE180C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4F9B6FE3-CAC4-4594-84AD-98D1ECDE180C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4F9B6FE3-CAC4-4594-84AD-98D1ECDE180C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4F9B6FE3-CAC4-4594-84AD-98D1ECDE180C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E8F8C13E-2E05-4092-84FD-B7B7B12DABBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8F8C13E-2E05-4092-84FD-B7B7B12DABBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E8F8C13E-2E05-4092-84FD-B7B7B12DABBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E8F8C13E-2E05-4092-84FD-B7B7B12DABBB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {52986844-698F-486B-AEC9-846AAF50CF46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {52986844-698F-486B-AEC9-846AAF50CF46}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {52986844-698F-486B-AEC9-846AAF50CF46}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {52986844-698F-486B-AEC9-846AAF50CF46}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0C1A05D2-653D-4F88-B397-BA53E0BA7281}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0C1A05D2-653D-4F88-B397-BA53E0BA7281}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0C1A05D2-653D-4F88-B397-BA53E0BA7281}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0C1A05D2-653D-4F88-B397-BA53E0BA7281}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D0EE40C1-87EF-413D-A046-DC37D58BC344}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D0EE40C1-87EF-413D-A046-DC37D58BC344}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D0EE40C1-87EF-413D-A046-DC37D58BC344}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D0EE40C1-87EF-413D-A046-DC37D58BC344}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {6F017146-B95A-4081-9CC0-B0245F78D72B} = {F41DA3D8-A0E9-4A05-8A35-78313C0F5804}
- {1A0CB973-23C9-4A17-905E-59510CD18932} = {F41DA3D8-A0E9-4A05-8A35-78313C0F5804}
- {6B92CC17-B7DB-446F-BF2F-A93696D48B5D} = {F41DA3D8-A0E9-4A05-8A35-78313C0F5804}
{B09236CB-BBD2-4DCF-A698-74CCCAB29FFB} = {07BAE427-96CF-4F9B-80A9-48CFB0A89CF3}
{1E319404-8EF0-40A1-A9D7-A404A71A98C4} = {07BAE427-96CF-4F9B-80A9-48CFB0A89CF3}
{39FD806A-D320-43C9-800F-E9D9BCC90623} = {AF995FEF-BB94-48D0-B02B-6671DA73056B}
{FE039675-00BE-4DC5-945F-14E7625170D0} = {07BAE427-96CF-4F9B-80A9-48CFB0A89CF3}
+ {4F9B6FE3-CAC4-4594-84AD-98D1ECDE180C} = {F41DA3D8-A0E9-4A05-8A35-78313C0F5804}
+ {E8F8C13E-2E05-4092-84FD-B7B7B12DABBB} = {F41DA3D8-A0E9-4A05-8A35-78313C0F5804}
+ {52986844-698F-486B-AEC9-846AAF50CF46} = {F41DA3D8-A0E9-4A05-8A35-78313C0F5804}
+ {0C1A05D2-653D-4F88-B397-BA53E0BA7281} = {F41DA3D8-A0E9-4A05-8A35-78313C0F5804}
+ {D0EE40C1-87EF-413D-A046-DC37D58BC344} = {07BAE427-96CF-4F9B-80A9-48CFB0A89CF3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C0FF62D6-1374-4939-A546-432862338528}
diff --git a/index.md b/index.md
index aeb661e..3d66219 100644
--- a/index.md
+++ b/index.md
@@ -1,518 +1,596 @@
# Developer Guide
+
## i. Installation
-Install the latest nuget package as appropriate.
-`Scemio.Core` - for installing schemio for `bespoke` implementation of query engine.
+`Schemio` allows you to aggregate data from heterogeneous data stores offering `SQL` & `API` packages out of the box below. SQL queries are supported by `Dapper` and `EntityFramework` engines. You could also `extend` Schemio to provide your own implementation(s) of `Query` and supporting `Query Engine` to retrieve data from `custom` data store(s).
+
+Below are the Nuget packages available.
+
+`Scemio.Core` - Install to extend schemio to implement `custom` querying engine.
```
NuGet\Install-Package Schemio.Core
```
-`Schemio.SQL` - for installing schemio for SQL with `Dapper` engine.
+`Schemio.SQL` - Install when you would like to include SQL `Dapper` queries to access SQL database.
```
NuGet\Install-Package Schemio.SQL
```
-`Schemio.EntityFramework` - for installing schemio for SQL with `EntityFramework` engine.
+`Schemio.EntityFramework` - Install when you would like to include SQL `EntityFramework` queries to access SQL database.
```
NuGet\Install-Package Schemio.EntityFramework
```
-## ii. Implementation: Usingse Schemio
-
-To use schemio you need to
-> Step 1 - Setup the entity to be fetched.
->
-> Step 2 - Construct the `DataProvider` with required dependencies.
-
-### Step 1. Entity Setup
-Setting up an entity includes the following.
-* Define the `entity` to be fetched using `DataProvider` - which is basically a class with entire object graph (with nested typed properties).
-* Define the `entity schema` which is schema path mapping of the entire entity object graph. Each mapping consists of a `query` and `transformer` pair mapped to a sections of object graph (using XPaths or JsonPath for schema paths)
-
-#### 1.1 Entity
-To mark the class as Entity, implement the class from `IEntity` interface.
-Bear in mind this is the root entity to be fetched.
-
->Below is an example `Customer` entity.
->
-> ```
-> public class Customer : IEntity
-> {
-> public int CustomerId { get; set; }
-> public string CustomerCode { get; set; }
-> public string CustomerName { get; set; }
-> public Communication Communication { get; set; }
-> public Order[] Orders { get; set; }
-> }
-> ```
+`Schemio.Api` - Install when you would like to include web api queries with `HttpClient` query engine.
+```
+NuGet\Install-Package Schemio.Api
+```
+## ii. Implementation: Using Schemio
+
+To use **Schemio** you need to do the below steps
+- **Step 1**: Define the aggregated `Entity`.
+- **Step 2**: Setup the aggregate `Configuration` comprising of `Query`/`Transformer` hierarchical nested mappings.
+- **Step 3**: Construct the `DataProvider` with required dependencies.
+
+### Step 1. Define Aggregate Entity.
+To create an aggregate `Entity`, implement the class from `IEntity` interface. This is the entity that will be returned as aggregated result from multiple queries assembled to execute against homogeneous or heterogeneous data storage's.
+
+Below is an example `Customer` entity.
+
+```
+public class Customer : IEntity
+{
+ public int CustomerId { get; set; }
+ public string CustomerCode { get; set; }
+ public string CustomerName { get; set; }
+ public Communication Communication { get; set; }
+ public Order[] Orders { get; set; }
+}
+```
For the customer class, we can see there are three levels of nesting in the object graph.
- Level 1 with paths: `Customer`
- Level 2 with paths: `Customer.Communication` and `Customer.Orders`
- Level 3 with paths: `Customer.Orders.Items`
-If we choose XML Schema Definition (XSD) for the object schema of the above Customer class fo mapping with XPATHs
-then below is the Customer XSD and XPaths for different nesting levels.
-
-> Customer XSD is
-> ```
-> Coming soon...
-> ```
-
-> Schema mappings using XPaths are
-> ```
-> - Level 1 with XPath: Customer
-> - Level 2 with XPaths: Customer/Communication and Customer/Orders
-> - Level 3 with XPath: Customer/Orders/Order/Items/Item
-
-#### 1.2 Entity Schema Definition
-Define entity schema definition for the entity in context.
-
-* `Entity schema definition` is basically a configuration with hierarchy of `query/transformer` pairs mapped to the schema paths pointing to different levels of the entity's object graph.
-* `Query` is an implementation to fetch data for a certain section of object graph from an underlying data storage.
-* `Transformer` is an implementation to map the data fetched by the linked query to the relevant sections of the entity's object graph.
-
-To define Entity schema, implement `BaseEntitySchema` interface where T is entity in context.
-
->
-Example Entity Schema Definition (using XPaths)
-> The `Customer` entity with `three` levels of `nesting` is configured below in `CustomerSchema` definition to show `query/transformer` pairs nested accordingly mapping to object graph using the XPath definitions.
->
-> ```
-> internal class CustomerSchema : BaseEntitySchema
-> {
-> public override IEnumerable> GetSchema()
-> {
-> return CreateSchema.For()
-> .Map(For.Paths("customer"),
-> customer => customer.Dependents
-> .Map(For.Paths("customer/communication"))
-> .Map(For.Paths("customer/orders"),
-> customerOrders => customerOrders.Dependents
-> .Map(For.Paths("customer/orders/order/items")))
-> ).Create();
-> }
-> }
->```
-
-##### i. Query/Transformer Mapping
-Every `Query` type in the `EntitySchema` definition should have a complementing `Transformer` type.
-You could map multiple `schema paths` to a given query/transformer pair. Currently, `XPath` and `JSONPath` schema languages are supported.
+If we choose XML Schema Definition (XSD) for the Customer entity then XPaths for nesting levels should be.
+```
+- Level 1 with XPath: Customer
+- Level 2 with XPaths: Customer/Communication and Customer/Orders
+- Level 3 with XPath: Customer/Orders/Order/Items/Item
+```
+### Step 2: Setup Entity Aggregate Configuration
+To define `Entity Configuration`, derive from `EntityConfiguration` class where `TEntity` is aggregate entity in context (ie. `IEntity`).
->Below is the snippet from `CustomerSchema` definition.
->```
-> .Map(For.Paths("customer", "customer/code", "customer/name"))
->```
+The `Entity Configuration` is basically `hierarchies` of `Query` & `Transformer` pairs mapped to the schema `paths` pointing to various `nesting` levels in the entity's object graph.
+* `Query` is an implementation to `fetch` data for mapped sections of object graph.
+* `Transformer` is an implementation to `map` data fetched by the associated query to the relevant sections of the entity's object graph.
-##### ii. Nested Query/Transformer Mappings
-* You could nest query/transformer pairs in a `parent/child` hierarchy. In which case the output of the parent query will serve as the input to the child query to resolve its query paramter.
-* The query/transformer mappings can be `nested` to `5` levels down.
-* When certain `schema paths` are included in the DataProvider `request` to fetch the Entity, the relevant query and transformer pairs get executed in the order of their nesting to hydrate the entity.
+Below is an example Entity Configuration for the Customer Entity.
->Example nesting of Communication query under Customer query.
->```
-> .Map(For.Paths("customer"), -- Parent
-> customer => customer.Dependents
-> .Map(For.Paths("customer/communication")) -- Child
->```
-
-
-Please see the execution sequence below for queries and transformers nested in CustomerSchema implemented above.
-
-
-
-
-`Please Note:` If you need to support custom schema language for mapping the object graph, then see extending schemio section below.
-
-
-#### 1.2.1 Query Class
-The purpose of a query class is to execute with supported QueryEngine to fetch data from data storage.
-
-QueryEngine is an implementation of `IQueryEngine` to execute queries against a supported data storage to return a collection of query results (ie. of type IQueryResult).
-
-As explained above, You can configure a query in `Parent` or `Child` (nested) mode in nested hierarchies.
-
-i. Parent Query
-
-To define a `parent` or `root` query which is usually configured at level 1 to query the root entity, derive from `aseRootQuery`
-* `TQueryParameter` is basically the class that holds the `inputs` required by the root query for execution. It is an implementation of `IQueryParameter` type.
-* `TQueryResult` is the result that will be returned from executing the root query. It is an implementation of `IQueryResult` type.
-
-The query parameter needs to be resolved before executing the query with QueryEngine.
-
-In `parent` mode, the query parameter is resolved using the `IDataContext` parameter passed to data provider class.
-
-
-> See example `CustomerQuery` implemented to be configured and run in parent mode below.
-> ```
->internal class CustomerQuery : BaseRootQuery
-> {
-> public override void ResolveRootQueryParameter(IDataContext context)
-> {
-> // Executes as Parent or Level 1 query.
-> // The query parameter is resolved using IDataContext parameter of data provider class.
->
-> var customer = (CustomerContext)context;
-> QueryParameter = new CustomerParameter
-> {
-> CustomerId = customer.CustomerId
-> };
-> }
-> }
->```
-
-ii. Child Query
-
-To define a `child` or `dependant` query which is usually configured as child at level below the root query to query, derive from `BaseChildQuery`
-* `TQueryParameter` is basically the class that holds the `inputs` required by the child query for execution. It is an implementation of `IQueryParameter` type.
-* `TQueryResult` is the result that will be returned by executing the child query. It is an implementation of `IQueryResult` type.
-
-Similar to Root query, the query parameter of child query needs to be resolved before executing with QueryEngine.
-
-In `child` mode, the query parameter is resolved using the `query result` of the `parent` query. You can have a maximum of `5` levels of query nestings.
-
-> See example `CustomerCommunicationQuery` implemented to be configured and run as child or nested query to customer query below. Please see `CustomerSchema` definition above for parent/child configuration setup.
->```
-> internal class CustomerCommunicationQuery : BaseChildQuery
-> {
-> public override void ResolveChildQueryParameter(IDataContext context, IQueryResult parentQueryResult)
-> {
-> // Execute as child to customer query.
-> // The result from parent customer query is used to resolve the query parameter of the nested communication query.
->
-> var customer = (CustomerResult)parentQueryResult;
-> QueryParameter = new CustomerParameter
-> {
-> CustomerId = customer.Id
-> };
-> }
-> }
->```
-
-#### Query Engines
-
-`Please Note:` The above query implementation examples are with respect to parent/child configuration. The actual storage specific query definition should vary with specific implementation of the QueryEngine.
-> Please see supported Query engine implementations below.
-- `Schemio.SQL` - provides the implementation of IQueryEngine to execute SQL queries. Uses `Dapper` for SQL data acess.
-- `Schemio.EntityFramework` - provides implementation of IQueryEngine to execute `Entity Framework` queries.
-
-`Query using Schemio.SQL`
-The SQL query needs to implement `BaseSQLRootQuery` or `BaseSQLChildQuery` based on parent or child implementation.
-And, requires implementing `public abstract CommandDefinition GetCommandDefinition()` method to return `command definition` for query to be executed with `Dapper` supported QueryEngine.
-
-See below example `CustomerQuery` implemented as Root SQL query
->```
-> internal class CustomerQuery : BaseSQLRootQuery
-> {
-> public override void ResolveRootQueryParameter(IDataContext context)
-> {
-> // Executes as root or level 1 query.
-> var customer = (CustomerContext)context.Entity;
-> QueryParameter = new CustomerParameter
-> {
-> CustomerId = (int)customer.CustomerId
-> };
-> }
->
-> public override IEnumerable Execute(IDbConnection conn)
-> {
-> return conn.Query(new CommandDefinition
-> (
-> "select CustomerId as Id, " +
-> "Customer_Name as Name," +
-> "Customer_Code as Code " +
-> $"from TCustomer where customerId={QueryParameter.CustomerId}"
-> ));
-> }
-> }
->```
->
-See below example `CustomerOrderItemsQuery` implemented as child SQL query.
->```
->internal class CustomerOrderItemsQuery : BaseSQLChildQuery
-> {
-> public override void ResolveChildQueryParameter(IDataContext context, IQueryResult parentQueryResult)
-> {
-> // Execute as child query to order query taking OrderResult to resolve query parameter.
-> var ordersResult = (OrderResult)parentQueryResult;
->
-> QueryParameter ??= new OrderItemParameter();
-> QueryParameter.OrderIds.Add(ordersResult.OrderId);
-> }
->
-> public override IEnumerable Execute(IDbConnection conn)
-> {
-> return conn.Query(new CommandDefinition
-> (
-> "select OrderId, " +
-> "OrderItemId as ItemId, " +
-> "Name, " +
-> "Cost " +
-> $"from TOrderItem where OrderId in ({QueryParameter.ToCsv()})"
-> ));
-> }
-> }
->```
-
-`Query using Schemio.EntityFramework`
-The SQL query needs to implement `BaseSQLRootQuery` or `BaseSQLChildQuery` based on parent or child implementation.
-And, requires implementing `public abstract IEnumerable Run(DbContext dbContext)` method to implement query using `DbContext` using entity framework.
-
-See below example `CustomerQuery` implemented as Root Entity framework query
->```
-> internal class CustomerQuery : BaseSQLRootQuery
-> {
-> public override void ResolveRootQueryParameter(IDataContext context)
-> {
-> // Executes as root or level 1 query.
-> var customer = (CustomerContext)context.Entity;
-> QueryParameter = new CustomerParameter
-> {
-> CustomerId = (int)customer.CustomerId
-> };
-> }
->
-> public override IEnumerable Run(DbContext dbContext)
-> {
-> return dbContext.Set()
-> .Where(c => c.Id == QueryParameter.CustomerId)
-> .Select(c => new CustomerResult
-> {
-> Id = c.Id,
-> Name = c.Name,
-> Code = c.Code
-> });
-> }
-> }
->```
->
-See below example `CustomerOrderItemsQuery` implemented as child Entity framework query.
->```
->internal class CustomerOrderItemsQuery : BaseSQLChildQuery
-> {
-> public override void ResolveChildQueryParameter(IDataContext context, IQueryResult parentQueryResult)
-> {
-> // Execute as child query to order query taking OrderResult to resolve query parameter.
-> var ordersResult = (CustomerOrderResult)parentQueryResult;
->
-> QueryParameter ??= new OrderItemParameter();
-> QueryParameter.OrderIds.Add(ordersResult.OrderId);
-> }
->
-> public override IEnumerable Run(DbContext dbContext)
-> {
-> return dbContext.Set()
-> .Where(p => QueryParameter.OrderIds.Contains(p.Order.OrderId))
-> .Select(c => new OrderItemResult
-> {
-> ItemId = c.ItemId,
-> Name = c.Name,
-> Cost = c.Cost,
-> OrderId = c.Order.OrderId
-> });
-> ;
-> }
-> }
->```
-
-
-#### 2.2 Tranformer Class
-The purpose of the transformer class is to transform the data fetched by the linked query class and mapp to the configured object graph of the entity.
+```
+internal class CustomerConfiguration : EntityConfiguration
+{
+ public override IEnumerable> GetSchema()
+ {
+ return CreateSchema.For()
+ .Map(For.Paths("customer"),
+ customer => customer.Dependents
+ .Map(For.Paths("customer/communication"))
+ .Map(For.Paths("customer/orders"),
+ customerOrders => customerOrders.Dependents
+ .Map(For.Paths("customer/orders/order/items"))))
+ .End();
+ }
+}
+```
+`CustomerConfiguration` shows `query/transformer` pairs mapped at three levels of nesting as per the `Customer` entity object graph.
+`XPaths` are used to identify the schema paths in the object graph. Alternately, you could use your own representation to name the pairs or map the object graph. However, you would need to provide the `ISchemaPathmatcher` implementation to managing path matching.
-To define a transformer class, you need to implement `BaseTransformer`
-- where TEntity is Entity implementing `IEntity`. eg. Customer.
-- where TQueryResult is Query Result from associated Query. It is an implementation of `IQueryResult` interface.
+#### i. Query/Transformer Mapping
+Every `Query` type in the Entity `Configuration` definition should have a complementing `Transformer` type.
+You could map multiple `schema paths` to a given query/transformer pair. Currently, `XPath` and `JSONPath` schema languages are supported.
-Note: It is `important` that the transformer should map data only to the `schema path(s)` pointing `section(s)` of the object graph.
+Below is the snippet from `CustomerConfiguration` definition shows that `CustomerQuery` has associated `CustomerTransform` and the pair is mapped to the root `Customer` object.
+```
+ .Map(For.Paths("customer"))
+```
-For the example query/transformer mapping
->```
-> .Map(For.Paths("customer"))
->```
+#### ii. Nested Query/Transformer Mappings
+You could nest query/transformer pairs in a `parent/child` hierarchy. In which case the output of the parent query will serve as the input to the child query to resolve its query context.
-The customer transformer maps data only to the `customer` xpath mapped object graph of customer class.
-ie. - `customer/id`, `customer/customercode`, `customer/customername`
+The query/transformer mappings can be `nested` to `5` levels down.
+
+Below is snippet to show nesting of `CommunicationQuery` as child to `CustomerQuery`.
+```
+.Map(For.Paths("customer"), -- Parent
+ customer => customer.Dependents
+ .Map(For.Paths("customer/communication")) -- Child
+```
-In below transformer example, `CustomerTransformer` is implemented to transform entity `Customer` with `CustomerResult` query result obtained from `CustomerQuery` execution.
+Execution Flow
+* In parent/child hierarchy, the first parent query executes first, followed by its immediate children. The execution flows in sequence to the last child query in order of its nesting.
+* While executing the output of the parent is passed in to the child query to resolve query context and get it ready for execution.
+* Transformers are also executed in the same sequence to map data to the Aggregate Entity.
+* When a query path for nested query is included for execution, all the parent queries involved in that object graph get included for execution in order of its nesting.
->
->```
->internal class CustomerTransform : BaseTransformer
-> {
-> public override Customer Transform(CustomerResult queryResult, Customer entity)
-> {
-> var customer = entity ?? new Customer();
-> customer.CustomerId = queryResult.Id;
-> customer.CustomerName = queryResult.CustomerName;
-> customer.CustomerCode = queryResult.CustomerCode;
-> return customer;
-> }
-> }
->```
+Please see the execution sequence below for queries and transformers nested in `CustomerConfiguration` implemented above.
-### DataProvider Setup
-Data provider needs to setup with required dependencies. Provide implementations of below dependencies to construct the data provider.
+
-- `ILogger>` - logger implementation. default no logger.
-- `IEntitySchema` - mandatory entity schema definition for entity's object graph.
-- `IQueryEngine` - implementation of query engine to execute queries (of type IQuery) with supported data storage.
-- `ISchemaPathMatcher` - implementation of schema path matcher to use custom schema paths with entity schema definition.
+#### iii. Query Class
+`Query` - The purpose of a query class is to execute with supported QueryEngine to fetch data from data storage.
-Example constructors:
+`QueryEngine` is an implementation of `IQueryEngine` to execute queries with supported data storage to return query result (ie. Result instance of `IQueryResult`).
-i. With `EntitySchema` and `QueryEngine` implementations.
+Depending on the Nuget package(s) installed, you could implement `SQL` and `API` queries.
+* `SQL` queries execute to get data from SQL database using `Dapper` or `EntityFramework` engines.
+* `API` query executes web api to call an `endpoint` using `HTTPClient` supported engine to get data.
+**Important**: You can combine heterogeneous queries in the Entity configuration to target different data stores.
+
+Example of SQL & API queries are below.
+You need to override the `GetQuery(IDataContext context, IQueryResult parentQueryResult)` method to return query delegate (package specific implementation).
+* `IDataContext` is the context parameter passed to DataProvider to get aggregated results (. Aggregated Entity). This parameter is always available for both parent and child queries.
+* `IQueryResult` parameter is only available when query is configured in child mode, else will be null.
+
+##### `Schemio.SQL` - with `Dapper` Query implementation.
+To create a SQL query you need to derive from `SQLQuery` where TQueryResult is `IQueryResult` implementation.
+
+1. Example Parent Query - CustomerQuery
```
- public DataProvider(IEntitySchema entitySchema, params IQueryEngine[] queryEngines)
-```
-ii. With `Logger`, `EntitySchema`, `QueryEngine`, and `SchemaPathmMatcher` for custom schema paths mapping in entity schema definition.
+public class CustomerQuery : SQLQuery
+{
+ protected override Func> GetQuery(IDataContext context, IQueryResult parentQueryResult)
+ {
+ // Executes as root or level 1 query.
+ var customer = (CustomerRequest)context.Request;
+
+ return connection => connection.QueryFirstOrDefaultAsync(new CommandDefinition
+ (
+ "select CustomerId as Id, " +
+ "Customer_Name as Name," +
+ "Customer_Code as Code " +
+ $"from TCustomer where customerId={customer.CustomerId}"
+ ));
+ }
+}
```
- public DataProvider(ILogger> logger, IEntitySchema entitySchema, ISchemaPathMatcher schemaPathMatcher, params IQueryEngine[] queryEngines)
-
+2. Example Child Query - OrdersQuery
+```
+internal class OrdersQuery : SQLQuery>
+{
+ protected override Func>> GetQuery(IDataContext context, IQueryResult parentQueryResult)
+ {
+ // Execute as child to customer query.
+ var customer = (CustomerResult)parentQueryResult;
+
+ return async connection =>
+ {
+ var items = await connection.QueryAsync(new CommandDefinition
+ (
+ "select OrderId, " +
+ "OrderNo, " +
+ "OrderDate " +
+ "from TOrder " +
+ $"where customerId={customer.Id}"
+ ));
+
+ return new CollectionResult(items);
+ };
+ }
+}
```
-#### Schemio.SQL
-Construct DataProvider using `Schemio.SQL.QueryEngine` query engine.
+##### `Schemio.EntityFramework` - with `EntityFramework` Query implementation
+To create a SQL query you need to derive from `SQLQuery` where TQueryResult is `IQueryResult` implementation.
+
+1. Example Parent Query - CustomerQuery
+```
+public class CustomerQuery : SQLQuery
+{
+ protected override Func> GetQuery(IDataContext context, IQueryResult parentQueryResult)
+ {
+ // Executes as root or level 1 query. parentQueryResult will be null.
+ var customer = (CustomerRequest)context.Request;
+
+ return async dbContext =>
+ {
+ var result = await dbContext.Set()
+ .Where(c => c.Id == customer.CustomerId)
+ .Select(c => new CustomerResult
+ {
+ Id = c.Id,
+ Name = c.Name,
+ Code = c.Code
+ })
+ .FirstOrDefaultAsync();
+
+ return result;
+ };
+ }
+}
```
-var provider = new DataProvider(new CustomerSchema(), new Schemio.SQL.QueryEngine(new SQLConfiguration()));
+2. Example Child Query - OrdersQuery
```
+ internal class OrdersQuery : SQLQuery>
+ {
+ protected override Func>> GetQuery(IDataContext context, IQueryResult parentQueryResult)
+ {
+ // Execute as child to customer query.
+ var customer = (CustomerResult)parentQueryResult;
+
+ return async dbContext =>
+ {
+ var items = await dbContext.Set()
+ .Where(p => p.Customer.Id == customer.Id)
+ .Select(c => new OrderResult
+ {
+ CustomerId = c.CustomerId,
+ OrderId = c.OrderId,
+ Date = c.Date,
+ OrderNo = c.OrderNo
+ })
+ .ToListAsync();
+
+ return new CollectionResult(items);
+ };
+ }
+ }
+```
+##### `Schemio.Api` - with `HttpClient` Query implementation
+To create a Web API query you need to derive from `WebQuery` where TQueryResult is `IQueryResult` implementation.
-#### Schemio.EntityFramework
-Construct DataProvider using `Schemio.EntityFramework.QueryEngine` query engine.
+`Important`: If you need to get response headers in the result then TQueryResult should derive from `WebHeaderResult` class.
+1. Example Parent Query - CustomerQuery
```
-var provider = new DataProvider(new CustomerSchema(), Schemio.EntityFramework.QueryEngine());
+public class CustomerWebQuery : WebQuery
+{
+ public CustomerWebQuery() : base(Endpoints.BaseAddress)
+ {
+ }
+
+ protected override Func GetQuery(IDataContext context, IQueryResult parentApiResult)
+ {
+ // Executes as root or level 1 api.
+ var customerRequest = (CustomerRequest)context.Request;
+
+ return () => new Uri(string.Format(Endpoints.BaseAddress + Endpoints.Customer, customerRequest.CustomerId), UriKind.Absolute);
+ }
+
+ ///
+ /// Override to pass outgoing request headers.
+ ///
+ ///
+ protected override IDictionary GetRequestHeaders()
+ {
+ return new Dictionary
+ {
+ { "x-meta-branch-code", "London" }
+ };
+ }
+
+ ///
+ /// Override to subscribe for given Response headers to be added to TQueryResult.
+ /// For receiving response headers, You need to implement the TQueryResult type from `WebHeaderResult` class instead of IQueryResult.
+ ///
+ ///
+ protected override IEnumerable GetResponseHeaders()
+ {
+ return new[] { "x-meta-branch-code" };
+ }
+}
+```
+Note: CustomerResult is above query implements from `WebHeaderResult` class to support response headers.
+```
+public class CustomerResult : WebHeaderResult
+{
+ public int Id { get; set; }
+ public string Code { get; set; }
+ public string Name { get; set; }
+}
```
-### Using IOC for registrations
+2. Example Child Query - OrdersQuery
+```
+internal class OrdersQuery : WebQuery>
+{
+ public OrdersQuery() : base(Endpoints.BaseAddress)
+ {
+ }
+ protected override Func GetQuery(IDataContext context, IQueryResult parentQueryResult)
+ {
+ // Execute as child to customer api.
+ var customer = (CustomerResult)parentQueryResult;
+
+ return ()=> new Uri(string.Format($"v2/customers/{customer.Id}/orders);
+ }
+}
+```
-With ServiceCollection, you should call the `services.UseSchemio()` method for IoC registration.
+#### iv. Transformer Class
+The purpose of the transformer class is to transform the data fetched by the linked query class and map to the configured object graph of the entity.
-To configure Data provider with SQL Query engine, use fluent registration apis as shown below -
- ```
- services.UseSchemio(With.Schema(c => new CustomerSchema())
- .AddEngine(c => new QueryEngine(new SQLConfiguration { ConnectionSettings = new ConnectionSettings {
- Providername = "System.Data.SqlClient",
- ConnectionString ="Data Source=Powerstation; Initial Catalog=Customer; Integrated Security=SSPI;"
- }}))
- .LogWith(c => new Logger>(c.GetService())));
+To define a transformer class, you need to implement `BaseTransformer`
+- where TEntity is Aggregate Entity implementing `IEntity`. eg. Customer.
+- where TQueryResult is Query Result from associated Query. It is an implementation of `IQueryResult` interface.
+
+Example transformer - Customer Transformer
+```
+internal class CustomerTransform : BaseTransformer
+{
+ public override Customer Transform(CustomerResult queryResult, Customer entity)
+ {
+ var customer = entity ?? new Customer();
+ customer.CustomerId = queryResult.Id;
+ customer.CustomerName = queryResult.CustomerName;
+ customer.CustomerCode = queryResult.CustomerCode;
+
+ if (queryResult is WebHeaderResult webHeaderResult)
+ if (webHeaderResult.Headers.TryGetValue("x-meta-branch-code", out var branch))
+ customer.Branch = branch;
+
+ return customer;
+ }
+}
```
-To configure Data provider with Entity Framework Query engine, use fluent registration apis shown as below -
+**Note**: It is `important` that the transformer should map data only to the `schema path(s)` pointing `section(s)` of the object graph.
+
+For the example query/transformer mapping
+```
+.Map(For.Paths("customer/communication"))
+```
+The Communication transformer should map data only to the `customer/communication` xpath mapped object graph of customer class.
+
+### Step 3. DataProvider Setup
+Data provider needs to be setup with required dependencies. Provide implementations of below dependencies to construct the data provider.
+
+#### Container Registrations
+
+With ServiceCollection, you need to register the below dependencies.
+
+```
+ // Register core services
+ services.AddTransient(typeof(IQueryBuilder<>), typeof(QueryBuilder<>));
+ services.AddTransient(typeof(IEntityBuilder<>), typeof(EntityBuilder<>));
+ services.AddTransient(typeof(IDataProvider<>), typeof(DataProvider<>));
+ services.AddTransient();
+
+ // Register instance of ISchemaPathMatcher - Json, XPath or Custom.
+ services.AddTransient(c => new XPathMatcher());
+
+ // Enable logging
+ services.AddLogging();
+
+ //For Dapper SQL engine - Schemio.SQL
+ services.AddTransient(c => new QueryEngine(new SQLConfiguration { ConnectionSettings = new ConnectionSettings {
+ Providername = "System.Data.SqlClient",
+ ConnectionString ="Data Source=Powerstation; Initial Catalog=Customer; Integrated Security=SSPI;"
+ }});
+
+ // For entity framework engine - Schemio.EntityFramework
+ services.AddDbContextFactory(options => options.UseSqlServer(YourSqlConnection), ServiceLifetime.Scoped);
+ services.AddTransient(c => new QueryEngine(c.GetService>());
+
+ // For HTTPClient Engine for web APIs - Schemio.API
+
+ // Enable HttpClient
+ services.AddHttpClient();
+ services.AddTransient();
+
+ // Register each entity configuration. eg CustomerConfiguration
+ services.AddTransient, CustomerConfiguration>();
+
```
- services.AddDbContextFactory(options => options.UseSqlServer(YourSqlConnection), ServiceLifetime.Scoped);
- services.AddLogging();
+`Please Note:` You can combine multiple query engines and implement supporting types of queries to execute on target data platforms.
+
+
+#### Using Fluent interface for registrations
+
+i. Example registration: Schemio.SQL
- services.UseSchemio(With.Schema(c => new CustomerSchema())
- .AddEngine(c => new QueryEngine(c.GetService>()))
- .LogWith(c => new Logger>(c.GetService())));
+```
+// Enable DbProviderFactory.
+ DbProviderFactories.RegisterFactory(DbProviderName, SqliteFactory.Instance);
+
+ var connectionString = $"DataSource={Environment.CurrentDirectory}//Customer.db;mode=readonly;cache=shared";
+
+ var configuration = new SQLConfiguration { ConnectionSettings = new ConnectionSettings { ConnectionString = connectionString, ProviderName = DbProviderName } };
+
+// Enable logging
+ services.AddLogging();
+
+ services.UseSchemio()
+ .WithEngine(c => new QueryEngine(configuration))
+ .WithPathMatcher(c => new XPathMatcher())
+ .WithEntityConfiguration(c => new CustomerConfiguration());
+```
+
+ii. Example registration: Schemio.EntityFramework
```
+ var connectionString = $"DataSource={Environment.CurrentDirectory}//Customer.db;mode=readonly;cache=shared";
+
+// Enable DBContext Factory
+ services.AddDbContextFactory(options =>
+ options.UseSqlite(connectionString));
+
+// Enable logging
+ services.AddLogging();
+
+ services.UseSchemio()
+ .WithEngine(c => new QueryEngine(c.GetService>()))
+ .WithPathMatcher(c => new XPathMatcher())
+ .WithEntityConfiguration(c => new CustomerConfiguration());
+
+```
+iii. Example registration: Schemio.API
+```
+ // Enable logging
+ services.AddLogging();
+
+ // Enable HttpClient
+ services.AddHttpClient();
+
+ services.UseSchemio()
+ .WithEngine()
+ .WithPathMatcher(c => new XPathMatcher())
+ .WithEntityConfiguration(c => new CustomerConfiguration());
+
+```
+iv. Example registration: Multiple Engines
+```
+ // Enable logging
+ services.AddLogging();
+
+ // Enable HttpClient
+ services.AddHttpClient();
+
+ var connectionString = $"DataSource={Environment.CurrentDirectory}//Customer.db;mode=readonly;cache=shared";
+
+// Enable DBContext Factory
+ services.AddDbContextFactory(options =>
+ options.UseSqlite(connectionString));
+
+ services.UseSchemio()
+ .WithEngine()
+ .WithEngine(c => new QueryEngine(c.GetService>()))
+ .WithPathMatcher(c => new XPathMatcher())
+ .WithEntityConfiguration(c => new CustomerConfiguration());
+```
+
+#### Use Data Provider
+
+##### i. Dependency Inject - IDataProvider
-`Please Note:` You can combine multiple query engines and implement different types of queries to execute on different supported platforms.
+To use Data provider, Inject `IDataProvider` where T is IEntity, using constructor & property injection method or explicitly Resolve using service provider ie. `IServiceProvider.GetService(typeof(IDataProvider))`
-To use Data provider, Inject IDataProvider using constructor & property injection method or explicity Resolve using service provider ie. `IServiceProvider.GetService(typeof(IDataProvider<>))`
+##### ii. Call DataProvider.GetData(IEntityRequest context) method.
+You need to call the `GetData()` method with an instance of parameter class derived from `IEntityRequest` interface.
-## Extend Schemio
-### Custom Query Engine
-To provide custom query engine and query implementations, you need to extend the base interfaces as depicted below
-- IQueryEngine interface to implement the custom query engine to be used with schemio.
+The `IEntityRequest` provides a `SchemaPaths` property, which is a list of schema paths to include for the given request to fetch aggregated data.
+- When `no` paths are passed in the parameter then entire aggregated entity for all configured queries is returned.
+- When list of schema paths are included in the request then the returned aggregated data entity only includes query results from included queries.
+
+When nested path for a nested query is included (eg. customer/orders/order/items) then all parent queries in the respective parent paths also get included for execution.
+
+Example - Control Flow
+
+
+
+
+## Extending Schemio
+
+You could extend Schemio by providing your own custom implementation of the query engine (`IQueryEngine`) and query (`IQuery`) to execute queries on custom target data platform.
+
+To do this, you need to extend the base interfaces as depicted below.
+### i. IQueryEngine
+Implement `IQueryEngine` interface to provide the custom query engine to be used with schemio.
```
public interface IQueryEngine
- {
- ///
- /// Detrmines whether an instance of query can be executed with this engine.
- ///
- /// instance of IQuery.
- /// Boolean; True when supported.
- bool CanExecute(IQuery query);
-
- ///
- /// Executes a list of queries returning a list of aggregated results.
- ///
- /// List of IQuery instances.
- /// List of query results. Instances of IQueryResult.
- IEnumerable Execute(IEnumerable queries);
- }
+{
+ ///
+ /// Detrmines whether an instance of query can be executed with this engine.
+ ///
+ /// instance of IQuery.
+ /// Boolean; True when supported.
+ bool CanExecute(IQuery query);
+
+ ///
+ /// Executes a given query returning query result.
+ ///
+ /// Custom instance of IQuery.
+ /// Task of IQueryResult.
+ Task Execute(IQuery> query);
+}
```
Example Entity Framework implementation is below
```
public class QueryEngine : IQueryEngine where T : DbContext
+{
+ private readonly IDbContextFactory _dbContextFactory;
+
+ public QueryEngine(IDbContextFactory _dbContextFactory)
{
- private readonly IDbContextFactory _dbContextFactory;
+ this._dbContextFactory = _dbContextFactory;
+ }
+
+ public bool CanExecute(IQuery query) => query != null && query is ISQLQuery;
- public QueryEngine(IDbContextFactory _dbContextFactory)
+ public Task Execute(IQuery query)
+ {
+ using (var dbcontext = _dbContextFactory.CreateDbContext())
{
- this._dbContextFactory = _dbContextFactory;
+ var result = ((ISQLQuery)query).Run(dbcontext);
+ return result;
}
+ }
+}
+```
+### ii. IQuery
+With the Query Engine implementation, you also need to provide custom implementation of `IQuery` for executing the query with custom query engine.
- public bool CanExecute(IQuery query) => query != null && query is ISQLQuery;
+To do this, you need to extend `BaseQuery` where TQueryResult is `IQueryResult`. And, provide overrides for below methods.
+- `bool IsContextResolved()`
+Engine calls this method to confirm whether the query is ready for execution. Return true when query context is resolved.
+- `void ResolveQuery(IDataContext context, IQueryResult parentQueryResult)`
+This method is invoked by schemio to resolve the query context required for execution ith supporting query engine. `IQueryResult` parameter is only available when the custom query is configured in nested or child mode.
- public IEnumerable Execute(IEnumerable queries)
- {
- var output = new List();
- using (var dbcontext = _dbContextFactory.CreateDbContext())
- {
- foreach (var query in queries)
- {
- var results = ((ISQLQuery)query).Run(dbcontext);
+Example - EntityFramework Supported query implementation is shown below.
+```
+ public abstract class SQLQuery
+ : BaseQuery, ISQLQuery
+ where TQueryResult : IQueryResult
+ {
+ private Func> QueryDelegate = null;
+
+ public override bool IsContextResolved() => QueryDelegate != null;
+
+ public override void ResolveQuery(IDataContext context, IQueryResult parentQueryResult)
+ {
+ QueryDelegate = GetQuery(context, parentQueryResult);
+ }
+
+ async Task ISQLQuery.Run(DbContext dbContext)
+ {
+ return await QueryDelegate(dbContext);
+ }
+
+ ///
+ /// Get query delegate to return query result.
+ ///
+ ///
+ ///
+ ///
+ protected abstract Func> GetQuery(IDataContext context, IQueryResult parentQueryResult);
+ }
+```
- if (results == null)
- continue;
+### iii. Custom Schema Paths
- output.AddRange(results);
- }
+Additionally, You can use your own schema language instead of XPath/JSONPath to map aggregated entity's object graph, and register with schemio.
- return output.ToArray();
- }
- }
- }
-```
-- Provide base implementation supporting IQuery, IRootQuery & IChildQuery interfaces.
-- You can implement the parent and child base class implementations to construct for queries to be executed with custom engine implementation above.
+To do this you need to follow the below steps:
+#### i. Use schema paths in Entity Configuration.
-For Parent Query base implementation, see example below.
-```
-public abstract class BaseSQLRootQuery
- : BaseRootQuery, ISQLQuery
- where TQueryParameter : IQueryParameter
- where TQueryResult : IQueryResult
- {
- ///
- /// Get query delegate with implementation to return query result.
- /// Delegate returns a collection from db.
- ///
- /// Func>
- public abstract IEnumerable Run(DbContext dbContext);
- }
-```
-For Child Query implementation, see example below.
+Provide entity schema definition with query/transformer pairs using custom schema language paths.
+
+Example - with Dummy schema mapping
```
-public abstract class BaseSQLChildQuery
- : BaseChildQuery, ISQLQuery
- where TQueryParameter : IQueryParameter
- where TQueryResult : IQueryResult
- {
- ///
- /// Get query delegate with implementation to return query result.
- /// Delegate returns a collection from db.
- ///
- /// Func>
- public abstract IEnumerable Run(DbContext dbContext);
- }
+.Map(For.Paths("customer$orders"))
```
-### Custom Schema Language
-You can provide your own schema language support for use in entity schema definition to map sections of object graph.
+#### ii. ISchemaPathMatcher
+Provide implementation of `ISchemaPathMatcher` interface and implement `IsMatch()` method to provide logic for matching custom paths.
-To do this you need to follow the below steps
-* Provide entity schema definition with query/transformer pairs using custom schema language paths
-* Provide implementation of `ISchemaPathMatcher` interface and implement `IsMatch()` method to provide logic for matching custom paths. This matcher is used by query builder to pick queries for matched paths against the configured p in Entity schema definition.
+`Important`: This matcher is used by query builder to filter queries based matched paths, to include only required queries for execution to optimize performance.
```
public interface ISchemaPathMatcher
- {
- bool IsMatch(string inputPath, ISchemaPaths configuredPaths);
- }
+{
+ bool IsMatch(string inputPath, ISchemaPaths configuredPaths);
+}
```
-Example implementation of XPath matcher is below.
+Example implementation of XPathMatcher is below.
```
public class XPathMatcher : ISchemaPathMatcher
{
@@ -534,4 +612,3 @@ public class XPathMatcher : ISchemaPathMatcher
}
}
```
-
diff --git a/src/Schemio.API/IWebQuery.cs b/src/Schemio.API/IWebQuery.cs
new file mode 100644
index 0000000..81fc2d3
--- /dev/null
+++ b/src/Schemio.API/IWebQuery.cs
@@ -0,0 +1,12 @@
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Schemio.Core;
+
+namespace Schemio.API
+{
+ public interface IWebQuery : IQuery
+ {
+ Task Run(IHttpClientFactory httpClientFactory, ILogger logger = null);
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio.API/QueryEngine.cs b/src/Schemio.API/QueryEngine.cs
new file mode 100644
index 0000000..c6326b1
--- /dev/null
+++ b/src/Schemio.API/QueryEngine.cs
@@ -0,0 +1,32 @@
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Schemio.Core;
+using Schemio.Core.Helpers;
+
+namespace Schemio.API
+{
+ public class QueryEngine : IQueryEngine
+ {
+ private readonly ILogger logger;
+ private readonly IHttpClientFactory httpClientFactory;
+
+ public QueryEngine(IHttpClientFactory httpClientFactory, ILogger logger = null)
+ {
+ this.httpClientFactory = httpClientFactory;
+ this.logger = logger;
+
+ Constraints.NotNull(httpClientFactory);
+ }
+
+ public bool CanExecute(IQuery query) => query != null && query is IWebQuery;
+
+ public async Task Execute(IQuery query)
+ {
+ if (query == null || !(query is IWebQuery q))
+ return null;
+
+ return await q.Run(httpClientFactory, logger);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio.API/Schemio.API.csproj b/src/Schemio.API/Schemio.API.csproj
new file mode 100644
index 0000000..31f291e
--- /dev/null
+++ b/src/Schemio.API/Schemio.API.csproj
@@ -0,0 +1,50 @@
+
+
+
+ net9.0
+ true
+ enable
+ Schemio with EntityFramework
+ Code Shayk
+ Code Shayk
+ .Net Library to hydrate data entities by object graph using schema paths (supports XPath & JSONPath). Supports Web API using HttpClient.
+ Copyright (c) 2024 Code Shayk
+ https://github.com/CodeShayk/Schemio/wiki/i.-Home
+ ninja-icon-16.png
+ README.md
+ https://github.com/CodeShayk/Schemio
+ git
+ graphql json-schema xsd data-mapping query-engine data-mapper data-schema schema-mapping object-tree-query object-graph-query schema-mapper xsd-data-object object-graph-data entity-data entity-data-fetch hydrate-object object-hydration object-data object-fetch schemio-Api HttpCliet
+ LICENSE.md
+ True
+ True
+ snupkg
+ True
+ 2.0.0
+
+
+
+ True
+
+
+
+
+ True
+ \
+
+
+ True
+ \
+
+
+ True
+ \
+
+
+
+
+
+
+
+
+
diff --git a/src/Schemio.API/WebHeaderResult.cs b/src/Schemio.API/WebHeaderResult.cs
new file mode 100644
index 0000000..00f798b
--- /dev/null
+++ b/src/Schemio.API/WebHeaderResult.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using Schemio.Core;
+
+namespace Schemio.API
+{
+ ///
+ /// Implement to return web query response with headers.
+ ///
+ public abstract class WebHeaderResult : IQueryResult
+ {
+ public IDictionary Headers { get; internal set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio.API/WebQuery.cs b/src/Schemio.API/WebQuery.cs
new file mode 100644
index 0000000..ad6b34b
--- /dev/null
+++ b/src/Schemio.API/WebQuery.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Schemio.Core;
+using Schemio.Core.Helpers;
+
+namespace Schemio.API
+{
+ public abstract class WebQuery : BaseQuery, IWebQuery
+ where TQueryResult : IQueryResult
+ {
+ protected Uri BaseAddress;
+
+ protected WebQuery() : this(string.Empty)
+ {
+ }
+
+ protected WebQuery(string baseAddress)
+ {
+ if (!string.IsNullOrEmpty(baseAddress))
+ BaseAddress = new Uri(baseAddress);
+ }
+
+ private Func UriDelegate = null;
+
+ public override bool IsContextResolved() => UriDelegate != null;
+
+ public override void ResolveQuery(IDataContext context, IQueryResult parentQueryResult)
+ {
+ UriDelegate = GetQuery(context, parentQueryResult);
+ }
+
+ ///
+ /// Override to pass custom outgoing headers with the api request.
+ ///
+ ///
+ protected virtual IDictionary GetRequestHeaders()
+ {
+ return new Dictionary();
+ }
+
+ ///
+ /// Override to get custom incoming headers with the api response.
+ /// The headers collection will be present on `WebHeaderResult.Headers` when api response includes any of the headers defined in this method.
+ ///
+ ///
+ protected virtual IEnumerable GetResponseHeaders()
+ { return []; }
+
+ ///
+ /// Implement to construct the api web query.
+ ///
+ /// Request Context. Always available.
+ /// Result from parent Query. Only available when configured as nested web query. Else will be null.
+ ///
+ protected abstract Func GetQuery(IDataContext context, IQueryResult parentApiResult = null);
+
+ async Task IWebQuery.Run(IHttpClientFactory httpClientFactory, ILogger logger)
+ {
+ Constraints.NotNull(httpClientFactory);
+
+ logger?.LogInformation($"Run api: {GetType().Name}");
+
+ var Uri = UriDelegate();
+
+ if (Uri == null)
+ return null;
+
+ using (var client = httpClientFactory.CreateClient())
+ {
+ logger?.LogInformation($"Executing web api on thread {Thread.CurrentThread.ManagedThreadId} (task {Task.CurrentId})");
+
+ try
+ {
+ HttpResponseMessage result;
+
+ try
+ {
+ if (BaseAddress != null)
+ client.BaseAddress = BaseAddress;
+
+ var requestHeaders = GetRequestHeaders();
+
+ if (requestHeaders != null && requestHeaders.Any())
+ foreach (var header in requestHeaders)
+ client.DefaultRequestHeaders.Add(header.Key, header.Value);
+
+ result = await client.GetAsync(Uri);
+
+ var raw = result.Content.ReadAsStringAsync().Result;
+
+ if (!string.IsNullOrWhiteSpace(raw))
+ logger?.LogInformation($"Result.Content of executing web api: {Uri.AbsolutePath} is {raw}");
+
+ if (!result.IsSuccessStatusCode)
+ {
+ logger?.LogInformation($"Result of executing web api {Uri.AbsolutePath} is not success status code");
+ return null;
+ }
+
+ if (typeof(TQueryResult).UnderlyingSystemType != null && typeof(TQueryResult).UnderlyingSystemType.Name.Equals(typeof(CollectionResult<>).Name))
+ {
+ var typeArgs = typeof(TQueryResult).GetGenericArguments();
+ var arrType = typeArgs[0].MakeArrayType();
+ var arrObject = raw.ToObject(arrType);
+ if (arrObject != null)
+ {
+ var resultType = typeof(CollectionResult<>);
+ var collectionType = resultType.MakeGenericType(typeArgs);
+ var collectionResult = (TQueryResult)Activator.CreateInstance(collectionType, arrObject);
+
+ SetResponseHeaders(result, collectionResult);
+
+ return collectionResult;
+ }
+ }
+ else
+ {
+ var obj = raw.ToObject(typeof(TQueryResult));
+ if (obj != null)
+ {
+ var resObj = (TQueryResult)obj;
+ SetResponseHeaders(result, resObj);
+ return resObj;
+ }
+ }
+ }
+ catch (TaskCanceledException ex)
+ {
+ logger?.LogWarning(ex, $"An error occurred while sending the request. Query URL: {Uri.AbsolutePath}");
+ }
+ catch (HttpRequestException ex)
+ {
+ logger?.LogWarning(ex, $"An error occurred while sending the request. Query URL: {Uri.AbsolutePath}");
+ }
+ }
+ catch (AggregateException ex)
+ {
+ logger?.LogInformation($"Web api {GetType().Name} failed");
+ foreach (var e in ex.InnerExceptions)
+ logger?.LogError(e, "");
+ }
+ }
+
+ return null;
+ }
+
+ private void SetResponseHeaders(HttpResponseMessage response, TQueryResult? result)
+ {
+ if (response.Headers == null || result == null)
+ return;
+
+ var headers = GetResponseHeaders();
+
+ if (headers == null || !headers.Any())
+ return;
+
+ if (!(result is WebHeaderResult webResult))
+ throw new InvalidOperationException($"{typeof(TQueryResult).Name} should implement from WebHeaderResult for response Headers");
+
+ foreach (var header in headers)
+ {
+ if (!response.Headers.Any(r => r.Key == header))
+ continue;
+
+ var responseHeader = response.Headers.First(r => r.Key == header);
+
+ var value = responseHeader.Value != null && responseHeader.Value.Any()
+ ? responseHeader.Value.ElementAt(0)
+ : string.Empty;
+
+ webResult.Headers ??= new Dictionary();
+
+ webResult.Headers.Add(responseHeader.Key, value);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio/AssemblyInfo.cs b/src/Schemio.Core/AssemblyInfo.cs
similarity index 88%
rename from src/Schemio/AssemblyInfo.cs
rename to src/Schemio.Core/AssemblyInfo.cs
index 4005317..462744b 100644
--- a/src/Schemio/AssemblyInfo.cs
+++ b/src/Schemio.Core/AssemblyInfo.cs
@@ -11,8 +11,9 @@
// attribute to true on that type.
[assembly: ComVisible(false)]
-[assembly: InternalsVisibleTo("Schemio.Tests")]
+[assembly: InternalsVisibleTo("Schemio.Core.Tests")]
[assembly: InternalsVisibleTo("Schemio.SQL.Tests")]
+[assembly: InternalsVisibleTo("Schemio.EntityFramework.Tests")]
// The following GUID is for the ID of the typelib if this project is exposed to COM.
diff --git a/src/Schemio.Core/BaseQuery.cs b/src/Schemio.Core/BaseQuery.cs
new file mode 100644
index 0000000..11fb055
--- /dev/null
+++ b/src/Schemio.Core/BaseQuery.cs
@@ -0,0 +1,46 @@
+namespace Schemio.Core
+{
+ ///
+ /// Implement this base class to create a data provider query.
+ ///
+ ///
+ public abstract class BaseQuery : IQuery
+ where TQueryResult : IQueryResult
+ {
+ ///
+ /// Children queries dependent on this query.
+ ///
+ public List Children { get; set; }
+
+ ///
+ /// Get the result type for the query
+ ///
+ public Type ResultType
+ {
+ get { return typeof(TQueryResult); }
+ }
+
+ ///
+ /// Implement to determine whether the query context is resolved and ready to execute with supported engine.
+ ///
+ /// Boolean
+ public abstract bool IsContextResolved();
+
+ ///
+ /// Implement to resolve query context for execution with supporting query engine.
+ ///
+ /// data context passed to the data provider.
+ /// query result from parent query (when configured as nested query). Can be null.
+ public abstract void ResolveQuery(IDataContext context, IQueryResult parentQueryResult);
+
+ ///
+ /// Run query with supporting IQueryEngine instance.
+ ///
+ /// IQueryEngine Instance
+ /// Instance of IQueryResult
+ public Task Run(IQueryEngine engine)
+ {
+ return engine.Execute(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio/BaseTransformer.cs b/src/Schemio.Core/BaseTransformer.cs
similarity index 98%
rename from src/Schemio/BaseTransformer.cs
rename to src/Schemio.Core/BaseTransformer.cs
index 4bb2176..3669abf 100644
--- a/src/Schemio/BaseTransformer.cs
+++ b/src/Schemio.Core/BaseTransformer.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public abstract class BaseTransformer : ITransformer, ITransformerContext, ITransformerQueryResult
diff --git a/src/Schemio/CacheResultAttribute.cs b/src/Schemio.Core/CacheResultAttribute.cs
similarity index 72%
rename from src/Schemio/CacheResultAttribute.cs
rename to src/Schemio.Core/CacheResultAttribute.cs
index 0b32eb8..d989780 100644
--- a/src/Schemio/CacheResultAttribute.cs
+++ b/src/Schemio.Core/CacheResultAttribute.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public class CacheResultAttribute : Attribute
{ }
diff --git a/src/Schemio/ChildrenQueries.cs b/src/Schemio.Core/ChildrenQueries.cs
similarity index 87%
rename from src/Schemio/ChildrenQueries.cs
rename to src/Schemio.Core/ChildrenQueries.cs
index 42b30d9..9906175 100644
--- a/src/Schemio/ChildrenQueries.cs
+++ b/src/Schemio.Core/ChildrenQueries.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public class ChildrenQueries
{
diff --git a/src/Schemio.Core/CollectionResult.cs b/src/Schemio.Core/CollectionResult.cs
new file mode 100644
index 0000000..d5600b1
--- /dev/null
+++ b/src/Schemio.Core/CollectionResult.cs
@@ -0,0 +1,13 @@
+namespace Schemio.Core
+{
+ public class CollectionResult : List, IQueryResult
+ {
+ public CollectionResult(IEnumerable list) : base(list)
+ {
+ }
+
+ public CollectionResult()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio/CreateSchema.cs b/src/Schemio.Core/CreateSchema.cs
similarity index 96%
rename from src/Schemio/CreateSchema.cs
rename to src/Schemio.Core/CreateSchema.cs
index 309e324..8b0b7bc 100644
--- a/src/Schemio/CreateSchema.cs
+++ b/src/Schemio.Core/CreateSchema.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
#region Helpers
@@ -76,7 +76,7 @@ public IMapOrComplete Map(ISchemaPa
public Mappings GetMappings => this;
- public IEnumerable> Create() => this;
+ public IEnumerable> End() => this;
}
public class Mapping :
@@ -125,7 +125,7 @@ public interface IMapOrComplete : IMap> Create();
+ IEnumerable> End();
}
public interface IWithDependents
diff --git a/src/Schemio/DataContext.cs b/src/Schemio.Core/DataContext.cs
similarity index 55%
rename from src/Schemio/DataContext.cs
rename to src/Schemio.Core/DataContext.cs
index edb4020..2e528fe 100644
--- a/src/Schemio/DataContext.cs
+++ b/src/Schemio.Core/DataContext.cs
@@ -1,14 +1,14 @@
-namespace Schemio
+namespace Schemio.Core
{
internal class DataContext : IDataContext
{
- public DataContext(IEntityContext entityContext)
+ public DataContext(IEntityRequest request)
{
- Entity = entityContext;
+ Request = request;
Cache = new Dictionary();
}
public Dictionary Cache { get; set; }
- public IEntityContext Entity { get; set; }
+ public IEntityRequest Request { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Schemio/BaseEntitySchema.cs b/src/Schemio.Core/EntityConfiguration.cs
similarity index 77%
rename from src/Schemio/BaseEntitySchema.cs
rename to src/Schemio.Core/EntityConfiguration.cs
index d4b1b42..4e2c3d0 100644
--- a/src/Schemio/BaseEntitySchema.cs
+++ b/src/Schemio.Core/EntityConfiguration.cs
@@ -1,14 +1,14 @@
-namespace Schemio
+namespace Schemio.Core
{
///
/// Implement to configure schema path mappings for an Entity.
///
/// Entity type
- public abstract class BaseEntitySchema : IEntitySchema where TEntity : IEntity
+ public abstract class EntityConfiguration : IEntityConfiguration where TEntity : IEntity
{
public IEnumerable> Mappings { get; }
- public BaseEntitySchema()
+ public EntityConfiguration()
{
Mappings = GetSchema();
}
diff --git a/src/Schemio/Helpers/ArrayUtil.cs b/src/Schemio.Core/Helpers/ArrayUtil.cs
similarity index 95%
rename from src/Schemio/Helpers/ArrayUtil.cs
rename to src/Schemio.Core/Helpers/ArrayUtil.cs
index 01d6bd8..b9a7ae6 100644
--- a/src/Schemio/Helpers/ArrayUtil.cs
+++ b/src/Schemio.Core/Helpers/ArrayUtil.cs
@@ -1,4 +1,4 @@
-namespace Schemio.Helpers
+namespace Schemio.Core.Helpers
{
public static class ArrayUtil
{
diff --git a/src/Schemio.Core/Helpers/Constraints.cs b/src/Schemio.Core/Helpers/Constraints.cs
new file mode 100644
index 0000000..a6351a1
--- /dev/null
+++ b/src/Schemio.Core/Helpers/Constraints.cs
@@ -0,0 +1,11 @@
+namespace Schemio.Core.Helpers
+{
+ public static class Constraints
+ {
+ public static void NotNull(this T value)
+ {
+ if (value == null)
+ throw new ArgumentNullException(typeof(T).Name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio/Helpers/EnumerableExtentions.cs b/src/Schemio.Core/Helpers/EnumerableExtentions.cs
similarity index 96%
rename from src/Schemio/Helpers/EnumerableExtentions.cs
rename to src/Schemio.Core/Helpers/EnumerableExtentions.cs
index 5e7f7b8..d455d10 100644
--- a/src/Schemio/Helpers/EnumerableExtentions.cs
+++ b/src/Schemio.Core/Helpers/EnumerableExtentions.cs
@@ -1,6 +1,6 @@
using System.Text;
-namespace Schemio.Helpers
+namespace Schemio.Core.Helpers
{
public static class EnumerableExtentions
{
diff --git a/src/Schemio.Core/Helpers/JsonExtensions.cs b/src/Schemio.Core/Helpers/JsonExtensions.cs
new file mode 100644
index 0000000..d841e5e
--- /dev/null
+++ b/src/Schemio.Core/Helpers/JsonExtensions.cs
@@ -0,0 +1,11 @@
+using System.Text.Json;
+
+namespace Schemio.Core.Helpers
+{
+ public static class JsonExtensions
+ {
+ public static string? ToJson(this object value) => value != null ? JsonSerializer.Serialize(value) : null;
+
+ public static object? ToObject(this string value, Type type) => !string.IsNullOrEmpty(value) ? JsonSerializer.Deserialize(value, type) : null;
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio/Helpers/StringExtensions.cs b/src/Schemio.Core/Helpers/StringExtensions.cs
similarity index 83%
rename from src/Schemio/Helpers/StringExtensions.cs
rename to src/Schemio.Core/Helpers/StringExtensions.cs
index cce4dbf..2cefe2d 100644
--- a/src/Schemio/Helpers/StringExtensions.cs
+++ b/src/Schemio.Core/Helpers/StringExtensions.cs
@@ -1,4 +1,4 @@
-namespace Schemio.Helpers
+namespace Schemio.Core.Helpers
{
public static class StringExtensions
{
diff --git a/src/Schemio/Helpers/Xml/XDocumentExts.cs b/src/Schemio.Core/Helpers/Xml/XDocumentExts.cs
similarity index 96%
rename from src/Schemio/Helpers/Xml/XDocumentExts.cs
rename to src/Schemio.Core/Helpers/Xml/XDocumentExts.cs
index ad04e7d..936e178 100644
--- a/src/Schemio/Helpers/Xml/XDocumentExts.cs
+++ b/src/Schemio.Core/Helpers/Xml/XDocumentExts.cs
@@ -1,6 +1,6 @@
using System.Xml.Linq;
-namespace Schemio.Helpers.Xml
+namespace Schemio.Core.Helpers.Xml
{
public static class XDocumentExts
{
diff --git a/src/Schemio/Helpers/Xml/XmlHelper.cs b/src/Schemio.Core/Helpers/Xml/XmlHelper.cs
similarity index 94%
rename from src/Schemio/Helpers/Xml/XmlHelper.cs
rename to src/Schemio.Core/Helpers/Xml/XmlHelper.cs
index 1f1f9a4..b38db24 100644
--- a/src/Schemio/Helpers/Xml/XmlHelper.cs
+++ b/src/Schemio.Core/Helpers/Xml/XmlHelper.cs
@@ -2,7 +2,7 @@
using System.Xml;
using System.Xml.Serialization;
-namespace Schemio.Helpers.Xml
+namespace Schemio.Core.Helpers.Xml
{
public static class XmlHelper
{
diff --git a/src/Schemio/Helpers/Xml/XmlSanitizer.cs b/src/Schemio.Core/Helpers/Xml/XmlSanitizer.cs
similarity index 96%
rename from src/Schemio/Helpers/Xml/XmlSanitizer.cs
rename to src/Schemio.Core/Helpers/Xml/XmlSanitizer.cs
index 4b16ef1..24f3b1a 100644
--- a/src/Schemio/Helpers/Xml/XmlSanitizer.cs
+++ b/src/Schemio.Core/Helpers/Xml/XmlSanitizer.cs
@@ -2,7 +2,7 @@
using System.Text.RegularExpressions;
using System.Xml;
-namespace Schemio.Helpers.Xml
+namespace Schemio.Core.Helpers.Xml
{
public static class XmlSanitizer
{
diff --git a/src/Schemio/IDataContext.cs b/src/Schemio.Core/IDataContext.cs
similarity index 52%
rename from src/Schemio/IDataContext.cs
rename to src/Schemio.Core/IDataContext.cs
index d86f11f..e53967c 100644
--- a/src/Schemio/IDataContext.cs
+++ b/src/Schemio.Core/IDataContext.cs
@@ -1,7 +1,7 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IDataContext : IEntityContextCache
{
- IEntityContext Entity { get; }
+ IEntityRequest Request { get; }
}
}
\ No newline at end of file
diff --git a/src/Schemio/IDataProvider.cs b/src/Schemio.Core/IDataProvider.cs
similarity index 53%
rename from src/Schemio/IDataProvider.cs
rename to src/Schemio.Core/IDataProvider.cs
index cb6c7ba..ef3899b 100644
--- a/src/Schemio/IDataProvider.cs
+++ b/src/Schemio.Core/IDataProvider.cs
@@ -1,7 +1,7 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IDataProvider where TEntity : IEntity
{
- TEntity GetData(IEntityContext context);
+ TEntity GetData(IEntityRequest request);
}
}
\ No newline at end of file
diff --git a/src/Schemio/IEntity.cs b/src/Schemio.Core/IEntity.cs
similarity index 89%
rename from src/Schemio/IEntity.cs
rename to src/Schemio.Core/IEntity.cs
index 6319a64..efe74ed 100644
--- a/src/Schemio/IEntity.cs
+++ b/src/Schemio.Core/IEntity.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
///
/// Implement Entity required to be hydrated (using query/transformer).
diff --git a/src/Schemio.Core/IEntityBuilder.cs b/src/Schemio.Core/IEntityBuilder.cs
new file mode 100644
index 0000000..d375e1b
--- /dev/null
+++ b/src/Schemio.Core/IEntityBuilder.cs
@@ -0,0 +1,7 @@
+namespace Schemio.Core
+{
+ public interface IEntityBuilder where TEntity : IEntity
+ {
+ TEntity Build(IDataContext context, IList results);
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio/IEntitySchema.cs b/src/Schemio.Core/IEntityConfiguration.cs
similarity index 76%
rename from src/Schemio/IEntitySchema.cs
rename to src/Schemio.Core/IEntityConfiguration.cs
index aa96154..a7e34c1 100644
--- a/src/Schemio/IEntitySchema.cs
+++ b/src/Schemio.Core/IEntityConfiguration.cs
@@ -1,10 +1,10 @@
-namespace Schemio
+namespace Schemio.Core
{
///
/// Implement to configure schema path mappings for an Entity.
///
/// Entity type
- public interface IEntitySchema where TEntity : IEntity
+ public interface IEntityConfiguration where TEntity : IEntity
{
///
/// Entity schema mappings.
diff --git a/src/Schemio/IEntityContextCache.cs b/src/Schemio.Core/IEntityContextCache.cs
similarity index 91%
rename from src/Schemio/IEntityContextCache.cs
rename to src/Schemio.Core/IEntityContextCache.cs
index 60a5955..b4bbd9a 100644
--- a/src/Schemio/IEntityContextCache.cs
+++ b/src/Schemio.Core/IEntityContextCache.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IEntityContextCache
{
diff --git a/src/Schemio.Core/IEntityContextValidator.cs b/src/Schemio.Core/IEntityContextValidator.cs
new file mode 100644
index 0000000..62f3d29
--- /dev/null
+++ b/src/Schemio.Core/IEntityContextValidator.cs
@@ -0,0 +1,7 @@
+namespace Schemio.Core
+{
+ public interface IEntityContextValidator
+ {
+ public void Validate(IEntityRequest context);
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio/IEntityContext.cs b/src/Schemio.Core/IEntityRequest.cs
similarity index 73%
rename from src/Schemio/IEntityContext.cs
rename to src/Schemio.Core/IEntityRequest.cs
index f15c465..f26d15f 100644
--- a/src/Schemio/IEntityContext.cs
+++ b/src/Schemio.Core/IEntityRequest.cs
@@ -1,6 +1,6 @@
-namespace Schemio
+namespace Schemio.Core
{
- public interface IEntityContext
+ public interface IEntityRequest
{
///
/// Entity schema paths for data retrieval.
diff --git a/src/Schemio/IPolymorphicResult.cs b/src/Schemio.Core/IPolymorphicResult.cs
similarity index 75%
rename from src/Schemio/IPolymorphicResult.cs
rename to src/Schemio.Core/IPolymorphicResult.cs
index 6e0181c..7d505a5 100644
--- a/src/Schemio/IPolymorphicResult.cs
+++ b/src/Schemio.Core/IPolymorphicResult.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IPolymorphicResult : IQueryResult
{
diff --git a/src/Schemio/IQuery.cs b/src/Schemio.Core/IQuery.cs
similarity index 59%
rename from src/Schemio/IQuery.cs
rename to src/Schemio.Core/IQuery.cs
index 5da4225..9fe0b58 100644
--- a/src/Schemio/IQuery.cs
+++ b/src/Schemio.Core/IQuery.cs
@@ -1,14 +1,16 @@
-namespace Schemio
+namespace Schemio.Core
{
///
/// Implement IQuery to fetch data using API or database.
///
- public interface IQuery
+ public interface IQuery : IQueryRunner
{
List Children { get; set; }
Type ResultType { get; }
bool IsContextResolved();
+
+ void ResolveQuery(IDataContext context, IQueryResult parentQueryResult = null);
}
}
\ No newline at end of file
diff --git a/src/Schemio/IQueryBuilder.cs b/src/Schemio.Core/IQueryBuilder.cs
similarity index 81%
rename from src/Schemio/IQueryBuilder.cs
rename to src/Schemio.Core/IQueryBuilder.cs
index 61223cc..8d7686d 100644
--- a/src/Schemio/IQueryBuilder.cs
+++ b/src/Schemio.Core/IQueryBuilder.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IQueryBuilder
{
diff --git a/src/Schemio/IQueryEngine.cs b/src/Schemio.Core/IQueryEngine.cs
similarity index 66%
rename from src/Schemio/IQueryEngine.cs
rename to src/Schemio.Core/IQueryEngine.cs
index a2e2535..acfb7cb 100644
--- a/src/Schemio/IQueryEngine.cs
+++ b/src/Schemio.Core/IQueryEngine.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IQueryEngine
{
@@ -12,8 +12,8 @@ public interface IQueryEngine
///
/// Executes a list of queries returning a list of aggregated results.
///
- /// List of IQuery instances.
- /// List of query results. Instances of IQueryResult.
- IEnumerable Execute(IEnumerable queries);
+ /// Instance of IQuery.
+ /// Instance of IQueryResult.
+ Task Execute(IQuery query);
}
}
\ No newline at end of file
diff --git a/src/Schemio/IQueryExecutor.cs b/src/Schemio.Core/IQueryExecutor.cs
similarity index 84%
rename from src/Schemio/IQueryExecutor.cs
rename to src/Schemio.Core/IQueryExecutor.cs
index 3085279..13ee57a 100644
--- a/src/Schemio/IQueryExecutor.cs
+++ b/src/Schemio.Core/IQueryExecutor.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IQueryExecutor
{
diff --git a/src/Schemio/IQueryList.cs b/src/Schemio.Core/IQueryList.cs
similarity index 94%
rename from src/Schemio/IQueryList.cs
rename to src/Schemio.Core/IQueryList.cs
index 82794d8..31c9244 100644
--- a/src/Schemio/IQueryList.cs
+++ b/src/Schemio.Core/IQueryList.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IQueryList
{
diff --git a/src/Schemio/IQueryParameter.cs b/src/Schemio.Core/IQueryParameter.cs
similarity index 69%
rename from src/Schemio/IQueryParameter.cs
rename to src/Schemio.Core/IQueryParameter.cs
index 68c0ed0..eda20dd 100644
--- a/src/Schemio/IQueryParameter.cs
+++ b/src/Schemio.Core/IQueryParameter.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IQueryParameter
{
diff --git a/src/Schemio/IQueryResult.cs b/src/Schemio.Core/IQueryResult.cs
similarity index 68%
rename from src/Schemio/IQueryResult.cs
rename to src/Schemio.Core/IQueryResult.cs
index fbfa3a6..41f1ba4 100644
--- a/src/Schemio/IQueryResult.cs
+++ b/src/Schemio.Core/IQueryResult.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
public interface IQueryResult
{
diff --git a/src/Schemio.Core/IQueryRunner.cs b/src/Schemio.Core/IQueryRunner.cs
new file mode 100644
index 0000000..13abbbb
--- /dev/null
+++ b/src/Schemio.Core/IQueryRunner.cs
@@ -0,0 +1,7 @@
+namespace Schemio.Core
+{
+ public interface IQueryRunner
+ {
+ Task Run(IQueryEngine engine);
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio.Core/ISchemaPathMatcher.cs b/src/Schemio.Core/ISchemaPathMatcher.cs
new file mode 100644
index 0000000..51e71de
--- /dev/null
+++ b/src/Schemio.Core/ISchemaPathMatcher.cs
@@ -0,0 +1,13 @@
+namespace Schemio.Core
+{
+ public interface ISchemaPathMatcher
+ {
+ ///
+ /// Determines whether there is a match for given input path vs configured paths for entity's object graph.
+ ///
+ /// Input path from IEntityRequest.SchemaPaths
+ /// Configured paths from EntityConfiguration
+ ///
+ bool IsMatch(string inputPath, ISchemaPaths configuredPaths);
+ }
+}
\ No newline at end of file
diff --git a/src/Schemio/ITransformer.cs b/src/Schemio.Core/ITransformer.cs
similarity index 95%
rename from src/Schemio/ITransformer.cs
rename to src/Schemio.Core/ITransformer.cs
index 9bbbb0b..76ae8ef 100644
--- a/src/Schemio/ITransformer.cs
+++ b/src/Schemio.Core/ITransformer.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
///
/// Implement transformer to map data from supported query result to entity in context.
diff --git a/src/Schemio/ITransformerContext.cs b/src/Schemio.Core/ITransformerContext.cs
similarity index 93%
rename from src/Schemio/ITransformerContext.cs
rename to src/Schemio.Core/ITransformerContext.cs
index dee4f46..df6a1a9 100644
--- a/src/Schemio/ITransformerContext.cs
+++ b/src/Schemio.Core/ITransformerContext.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
///
/// Implement to set transform with data context.
diff --git a/src/Schemio/ITransformerQueryResult.cs b/src/Schemio.Core/ITransformerQueryResult.cs
similarity index 92%
rename from src/Schemio/ITransformerQueryResult.cs
rename to src/Schemio.Core/ITransformerQueryResult.cs
index c9758c5..893fce6 100644
--- a/src/Schemio/ITransformerQueryResult.cs
+++ b/src/Schemio.Core/ITransformerQueryResult.cs
@@ -1,4 +1,4 @@
-namespace Schemio
+namespace Schemio.Core
{
///
/// Implement to get supported Query result.
diff --git a/src/Schemio/Impl/DataProvider.cs b/src/Schemio.Core/Impl/DataProvider.cs
similarity index 75%
rename from src/Schemio/Impl/DataProvider.cs
rename to src/Schemio.Core/Impl/DataProvider.cs
index 9fe3c2a..484c758 100644
--- a/src/Schemio/Impl/DataProvider.cs
+++ b/src/Schemio.Core/Impl/DataProvider.cs
@@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
-using Schemio.PathMatchers;
+using Schemio.Core.PathMatchers;
-namespace Schemio.Impl
+namespace Schemio.Core.Impl
{
public class DataProvider : IDataProvider
where TEntity : IEntity, new()
@@ -9,23 +9,23 @@ public class DataProvider : IDataProvider
private readonly ILogger> logger;
private readonly IQueryExecutor queryExecutor;
private readonly IQueryBuilder queryBuilder;
- private readonly ITransformExecutor transformExecutor;
+ private readonly IEntityBuilder entityBuilder;
public DataProvider(
- IEntitySchema entitySchema,
+ IEntityConfiguration entitySchema,
params IQueryEngine[] queryEngines)
: this(null, new QueryBuilder(entitySchema, new XPathMatcher()),
- new QueryExecutor(queryEngines), new TransformExecutor(entitySchema))
+ new QueryExecutor(queryEngines), new EntityBuilder(entitySchema))
{
}
public DataProvider(
ILogger> logger,
- IEntitySchema entitySchema,
+ IEntityConfiguration entitySchema,
ISchemaPathMatcher schemaPathMatcher,
params IQueryEngine[] queryEngines)
: this(logger, new QueryBuilder(entitySchema, schemaPathMatcher),
- new QueryExecutor(queryEngines), new TransformExecutor(entitySchema))
+ new QueryExecutor(queryEngines), new EntityBuilder(entitySchema))
{
}
@@ -33,17 +33,17 @@ public DataProvider(
ILogger> logger,
IQueryBuilder queryBuilder,
IQueryExecutor queryExecutor,
- ITransformExecutor transformExecutor)
+ IEntityBuilder entityBuilder)
{
this.logger = logger;
this.queryBuilder = queryBuilder;
this.queryExecutor = queryExecutor;
- this.transformExecutor = transformExecutor;
+ this.entityBuilder = entityBuilder;
}
- public TEntity GetData(IEntityContext entityContext)
+ public TEntity GetData(IEntityRequest request)
{
- var context = new DataContext(entityContext);
+ var context = new DataContext(request);
return GetData(context);
}
@@ -52,6 +52,7 @@ internal TEntity GetData(IDataContext context)
// Build queries for the data source based on the included xPaths
var watch = System.Diagnostics.Stopwatch.StartNew();
var queries = queryBuilder.Build(context);
+
watch.Stop();
logger?.LogInformation("Query builder executed in " + watch.ElapsedMilliseconds + " ms");
@@ -63,7 +64,7 @@ internal TEntity GetData(IDataContext context)
// Executes configured transformers to map query results to target entity
watch = System.Diagnostics.Stopwatch.StartNew();
- var entity = transformExecutor.Execute(context, results);
+ var entity = entityBuilder.Build(context, results);
watch.Stop();
logger?.LogInformation("Transform executor executed in " + watch.ElapsedMilliseconds + " ms");
diff --git a/src/Schemio/Impl/TransformExecutor.cs b/src/Schemio.Core/Impl/EntityBuilder.cs
similarity index 71%
rename from src/Schemio/Impl/TransformExecutor.cs
rename to src/Schemio.Core/Impl/EntityBuilder.cs
index ed55eb0..48e6427 100644
--- a/src/Schemio/Impl/TransformExecutor.cs
+++ b/src/Schemio.Core/Impl/EntityBuilder.cs
@@ -1,10 +1,10 @@
-namespace Schemio.Impl
+namespace Schemio.Core.Impl
{
- public class TransformExecutor : ITransformExecutor where T : IEntity, new()
+ public class EntityBuilder : IEntityBuilder where T : IEntity, new()
{
- private readonly IEntitySchema entitySchema;
+ private readonly IEntityConfiguration entitySchema;
- public TransformExecutor(IEntitySchema entitySchema)
+ public EntityBuilder(IEntityConfiguration entitySchema)
{
this.entitySchema = entitySchema;
}
@@ -15,7 +15,7 @@ public TransformExecutor(IEntitySchema entitySchema)
/// Entity Context
/// List of Query results
///
- public T Execute(IDataContext context, IList queryResults)
+ public T Build(IDataContext context, IList queryResults)
{
var entity = new T { /*Version = entitySchemaMapping.Version*/ };
@@ -39,11 +39,16 @@ public T Execute(IDataContext context, IList queryResults)
.ToList();
foreach (var queryResult in queryResults)
- transformers.Where(transformer => (transformer as ITransformerQueryResult)?.SupportedQueryResult == queryResult.GetType()).ToList()
+ transformers.Where(transformer => IsMatch(((ITransformerQueryResult)transformer).SupportedQueryResult, queryResult.GetType())).ToList()
.ForEach(supportedtransformer => supportedtransformer?.Transform(queryResult, entity));
}
return entity;
+
+ static bool IsMatch(Type transformer, Type queryResult)
+ {
+ return transformer == queryResult;
+ }
}
}
}
\ No newline at end of file
diff --git a/src/Schemio/Impl/EventAggregator.cs b/src/Schemio.Core/Impl/EventPublisher.cs
similarity index 82%
rename from src/Schemio/Impl/EventAggregator.cs
rename to src/Schemio.Core/Impl/EventPublisher.cs
index 4628367..f135e55 100644
--- a/src/Schemio/Impl/EventAggregator.cs
+++ b/src/Schemio.Core/Impl/EventPublisher.cs
@@ -1,10 +1,10 @@
-namespace Schemio.Impl
+namespace Schemio.Core.Impl
{
- public class EventAggregator
+ public class EventPublisher
{
private readonly ISubscriber subscriber;
- public EventAggregator(ISubscriber subscriber)
+ public EventPublisher(ISubscriber subscriber)
{
this.subscriber = subscriber;
}
diff --git a/src/Schemio/Impl/EventSubscriber.cs b/src/Schemio.Core/Impl/EventSubscriber.cs
similarity index 88%
rename from src/Schemio/Impl/EventSubscriber.cs
rename to src/Schemio.Core/Impl/EventSubscriber.cs
index 33baf8e..34a2668 100644
--- a/src/Schemio/Impl/EventSubscriber.cs
+++ b/src/Schemio.Core/Impl/EventSubscriber.cs
@@ -1,4 +1,4 @@
-namespace Schemio.Impl
+namespace Schemio.Core.Impl
{
public class EventSubscriber : ISubscriber
{
@@ -28,8 +28,8 @@ public void OnEventHandler(IDataContext context, ExecutorResultArgs args)
if (unresolved.ParentQueryResultType != result.GetType())
continue;
- foreach (var query in unresolved.Queries.Cast())
- query.ResolveChildQueryParameter(context, queryResult);
+ foreach (var query in unresolved.Queries)
+ query.ResolveQuery(context, queryResult);
}
}
@@ -48,8 +48,8 @@ public void OnEventHandler(IDataContext context, ExecutorResultArgs args)
!unresolved.ParentQueryResultType.IsAssignableFrom(result.GetType()))
continue;
- foreach (var query in unresolved.Queries.Cast())
- query.ResolveChildQueryParameter(context, queryResult);
+ foreach (var query in unresolved.Queries)
+ query.ResolveQuery(context, queryResult);
}
}
}
diff --git a/src/Schemio/Impl/QueryBuilder.cs b/src/Schemio.Core/Impl/QueryBuilder.cs
similarity index 68%
rename from src/Schemio/Impl/QueryBuilder.cs
rename to src/Schemio.Core/Impl/QueryBuilder.cs
index c79f3c2..2ed81c2 100644
--- a/src/Schemio/Impl/QueryBuilder.cs
+++ b/src/Schemio.Core/Impl/QueryBuilder.cs
@@ -1,11 +1,11 @@
-namespace Schemio.Impl
+namespace Schemio.Core.Impl
{
public class QueryBuilder : IQueryBuilder where T : IEntity
{
- private readonly IEntitySchema entitySchema;
+ private readonly IEntityConfiguration entitySchema;
private readonly ISchemaPathMatcher schemaPathMatcher;
- public QueryBuilder(IEntitySchema entitySchema, ISchemaPathMatcher schemaPathMatcher)
+ public QueryBuilder(IEntityConfiguration entitySchema, ISchemaPathMatcher schemaPathMatcher)
{
this.entitySchema = entitySchema;
this.schemaPathMatcher = schemaPathMatcher;
@@ -18,10 +18,12 @@ public QueryBuilder(IEntitySchema entitySchema, ISchemaPathMatcher schemaPath
///
public IQueryList Build(IDataContext context)
{
- var queries = GetMappedQueries(entitySchema.Mappings.ToList(), context);
+ var mappings = entitySchema.Mappings.ToList();
- foreach (var query in queries.Queries.Cast())
- query.ResolveRootQueryParameter(context);
+ var queries = GetMappedQueries(mappings, context);
+
+ foreach (var query in queries.Queries)
+ query.ResolveQuery(context);
return new QueryList(queries.Queries);
}
@@ -32,16 +34,17 @@ private QueryList GetMappedQueries(IReadOnlyCollection>
for (var index = 1; index <= queryDependencyDepth; index++)
{
- var maps = mappings.Where(x => x.Order == index);
+ var maps = mappings.Where(x => x.Order == index).ToList();
foreach (var map in maps)
{
var dependentQueries =
mappings.Where(x => x.Order == index + 1 && x.DependentOn != null && x.DependentOn.GetType() == map.Query.GetType()).ToList();
- map.Query.Children ??= new List();
+ map.Query.Children = new List();
- map.Query.Children.AddRange(FilterByPaths(context, dependentQueries));
+ var filtered = FilterByPaths(context, dependentQueries);
+ map.Query.Children.AddRange(filtered);
}
}
@@ -54,8 +57,8 @@ private QueryList GetMappedQueries(IReadOnlyCollection>
private IEnumerable FilterByPaths(IDataContext context, IEnumerable> mappings)
{
- var matchedMappings = context.Entity.SchemaPaths != null
- ? mappings.Where(mapping => context.Entity.SchemaPaths.Any(Path => schemaPathMatcher.IsMatch(Path, mapping.SchemaPaths)))
+ var matchedMappings = context.Request?.SchemaPaths != null
+ ? mappings.Where(mapping => context.Request.SchemaPaths.Any(Path => schemaPathMatcher.IsMatch(Path, mapping.SchemaPaths)))
.ToList()
: mappings;
diff --git a/src/Schemio/Impl/QueryExecutor.cs b/src/Schemio.Core/Impl/QueryExecutor.cs
similarity index 76%
rename from src/Schemio/Impl/QueryExecutor.cs
rename to src/Schemio.Core/Impl/QueryExecutor.cs
index 41bbbdf..4991f17 100644
--- a/src/Schemio/Impl/QueryExecutor.cs
+++ b/src/Schemio.Core/Impl/QueryExecutor.cs
@@ -1,4 +1,4 @@
-namespace Schemio.Impl
+namespace Schemio.Core.Impl
{
public class QueryExecutor : IQueryExecutor
{
@@ -24,7 +24,7 @@ public IList Execute(IDataContext context, IQueryList queries)
private IQueryList Process(IDataContext context, IQueryList queries, List globalResults)
{
var subscriber = new EventSubscriber(queries.GetChildrenQueries());
- var eventAggregator = new EventAggregator(subscriber);
+ var eventAggregator = new EventPublisher(subscriber);
var results = RunQueries(queries);
@@ -42,7 +42,7 @@ private static void CacheResults(IDataContext context, List result
if (context.Cache == null)
return;
- foreach (var cacheResult in results.Where(result => result.GetType()
+ foreach (var cacheResult in results.Where(result => result != null && result.GetType()
.GetCustomAttributes(typeof(CacheResultAttribute), false)
.Any()))
if (!context.Cache.ContainsKey(cacheResult.GetType().Name))
@@ -55,12 +55,19 @@ private List RunQueries(IQueryList queryList)
foreach (var engine in queryEngines)
{
- var queries = queryList.Queries.Where(x => engine.CanExecute(x));
-
- var results = engine.Execute(queries);
-
- if (results != null)
- output.AddRange(results);
+ var tasks = queryList.Queries
+ .Where(x => engine.CanExecute(x))
+ .Select(s => s.Run(engine))
+ .ToList();
+
+ Task.WhenAll(tasks);
+
+ foreach (var task in tasks)
+ {
+ var result = task.Result;
+ if (result != null)
+ output.Add(result);
+ }
}
return output;
diff --git a/src/Schemio/Schemio.Core.csproj b/src/Schemio.Core/Motley - Backup.Core.csproj
similarity index 87%
rename from src/Schemio/Schemio.Core.csproj
rename to src/Schemio.Core/Motley - Backup.Core.csproj
index bc47a73..8d8a202 100644
--- a/src/Schemio/Schemio.Core.csproj
+++ b/src/Schemio.Core/Motley - Backup.Core.csproj
@@ -1,11 +1,10 @@
-
+