├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ └── config.yml ├── dependabot.yml └── workflows │ ├── merge-dependabot.yml │ └── on-push-do-docs.yml ├── .gitignore ├── code_of_conduct.md ├── docs ├── configuration.md ├── defining-graphs.md ├── filters.md ├── mdsource │ ├── configuration.source.md │ ├── defining-graphs.source.md │ ├── filters.source.md │ └── query-usage.source.md └── query-usage.md ├── license.txt ├── readme.md ├── readme.source.md └── src ├── .editorconfig ├── Directory.Build.props ├── Directory.Packages.props ├── GraphQL.EntityFramework.sln ├── GraphQL.EntityFramework.sln.DotSettings ├── GraphQL.EntityFramework ├── ComplexGraphResolver.cs ├── Compress.cs ├── ConnectionConverter.cs ├── EfDocumentExecuter.cs ├── EfGraphQLConventions.cs ├── Filters │ ├── Filters.cs │ └── ResolveFilters.cs ├── FodyWeavers.xml ├── GlobalUsings.cs ├── GraphApi │ ├── ConnectionBuilderEx.cs │ ├── EfGraphQLService.cs │ ├── EfGraphQLService_First.cs │ ├── EfGraphQLService_Navigation.cs │ ├── EfGraphQLService_NavigationConnection.cs │ ├── EfGraphQLService_NavigationList.cs │ ├── EfGraphQLService_Queryable.cs │ ├── EfGraphQLService_QueryableConnection.cs │ ├── EfGraphQLService_Single.cs │ ├── EfInterfaceGraphType.cs │ ├── EfObjectGraphType.cs │ ├── FieldBuilderEx.cs │ ├── IEfGraphQLService.cs │ ├── IEfGraphQLService_First.cs │ ├── IEfGraphQLService_Navigation.cs │ ├── IEfGraphQLService_NavigationConnection.cs │ ├── IEfGraphQLService_Queryable.cs │ ├── IEfGraphQLService_QueryableConnection.cs │ ├── IEfGraphQLService_Single.cs │ ├── QueryGraphType.cs │ ├── ResolveDbContext.cs │ ├── ResolveEfFieldContext.cs │ └── SingleEntityNotFoundException.cs ├── GraphQL.EntityFramework.csproj ├── GraphQlExtensions.cs ├── GraphTypeFinder.cs ├── Guard.cs ├── HttpContextCapture.cs ├── IncludeAppender.cs ├── InternalsVisibleTo.cs ├── KeyNameExtractor.cs ├── Mapping │ ├── GetGraphException.cs │ ├── Mapper.cs │ └── SimpleFieldResolver.cs ├── Navigation.cs ├── NavigationReader.cs ├── PropertyAccess │ ├── Property.cs │ └── PropertyCache.cs ├── QueryLogger.cs ├── Testing │ └── ClientQueryExecutor.cs └── Where │ ├── ArgumentProcessor.cs │ ├── ArgumentProcessor_List.cs │ ├── ArgumentProcessor_Queryable.cs │ ├── ArgumentReader.cs │ ├── ArgumentsAppender.cs │ ├── ExpressionBuilder.cs │ ├── ExpressionCache.cs │ ├── Graphs │ ├── Comparison.cs │ ├── ComparisonGraph.cs │ ├── Connector.cs │ ├── ConnectorGraph.cs │ ├── OrderBy.cs │ ├── OrderByGraph.cs │ ├── WhereExpression.cs │ └── WhereExpressionGraph.cs │ ├── ReflectionCache.cs │ ├── TypeConverter.cs │ └── WhereValidator.cs ├── SampleWeb.Tests ├── GlobalUsings.cs ├── GraphQlControllerTests.Companies_paging.verified.txt ├── GraphQlControllerTests.Complex_query_result.verified.txt ├── GraphQlControllerTests.Employee_summary.verified.txt ├── GraphQlControllerTests.First.verified.txt ├── GraphQlControllerTests.Get.verified.txt ├── GraphQlControllerTests.Post.verified.txt ├── GraphQlControllerTests.Single.verified.txt ├── GraphQlControllerTests.Single_not_found.verified.txt ├── GraphQlControllerTests.Variable.verified.txt ├── GraphQlControllerTests.cs ├── SampleWeb.Tests.csproj ├── SchemaPrint.Print.verified.txt └── SchemaPrint.cs ├── SampleWeb ├── DataContext │ ├── Company.cs │ ├── Device.cs │ ├── Employee.cs │ ├── OrderDetail.cs │ ├── SampleDbContext.cs │ └── StreetAddress.cs ├── DbContextBuilder.cs ├── FromGraphQL │ ├── ObjectDictionaryConverter.cs │ └── StringExtensions.cs ├── GlobalUsings.cs ├── GraphQlController.cs ├── Graphs │ ├── CompanyGraphType.cs │ ├── DeviceGraphType.cs │ ├── EmployeeGraphType.cs │ ├── EmployeeSummary.cs │ ├── EmployeeSummaryGraphType.cs │ └── OrderDetailGraphType.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Query.cs ├── SampleWeb.csproj ├── Schema.cs ├── Startup.cs └── Subscription.cs ├── Shared.sln.DotSettings ├── Snippets ├── Configuration.cs ├── ConnectionRootQuery.cs ├── ConnectionTypedGraph.cs ├── EnumerableConnection.cs ├── GlobalFilterSnippets.cs ├── GlobalUsings.cs ├── ResolveDbContextQuery.cs ├── RootQuery.cs ├── Snippets.csproj └── TypedGraph.cs ├── Tests ├── CompressTests.Simple.verified.txt ├── CompressTests.cs ├── ConnectionConverter │ ├── ConnectionConverterTests.List_first=10_after=1_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=10_after=null_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=11_after=1_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=11_after=null_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=1_after=0_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=2_after=1_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=2_after=null_last=null_before=2.verified.txt │ ├── ConnectionConverterTests.List_first=2_after=null_last=null_before=3.verified.txt │ ├── ConnectionConverterTests.List_first=2_after=null_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=3_after=1_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=3_after=null_last=null_before=2.verified.txt │ ├── ConnectionConverterTests.List_first=3_after=null_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=null_after=7_last=2_before=null.verified.txt │ ├── ConnectionConverterTests.List_first=null_after=null_last=2_before=8.verified.txt │ ├── ConnectionConverterTests.List_first=null_after=null_last=2_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=10_after=1_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=10_after=null_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=11_after=1_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=11_after=null_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=1_after=0_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=2_after=1_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=2_after=null_last=null_before=2.verified.txt │ ├── ConnectionConverterTests.Queryable_first=2_after=null_last=null_before=3.verified.txt │ ├── ConnectionConverterTests.Queryable_first=2_after=null_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=3_after=1_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=3_after=null_last=null_before=2.verified.txt │ ├── ConnectionConverterTests.Queryable_first=3_after=null_last=null_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=null_after=7_last=2_before=null.verified.txt │ ├── ConnectionConverterTests.Queryable_first=null_after=null_last=2_before=8.verified.txt │ ├── ConnectionConverterTests.Queryable_first=null_after=null_last=2_before=null.verified.txt │ └── ConnectionConverterTests.cs ├── DependencyResolutionTests │ ├── DependencyDbContext.cs │ ├── DependencyQuery.cs │ ├── DependencySchema.cs │ ├── DependencyTests.ExplicitModel.verified.txt │ ├── DependencyTests.ScopedDbContext.verified.txt │ ├── DependencyTests.SingletonDbContext.verified.txt │ ├── DependencyTests.TransientDbContext.verified.txt │ ├── DependencyTests.cs │ ├── Entity.cs │ └── EntityGraphType.cs ├── ExpressionBuilderTests.cs ├── GlobalFiltersTests.cs ├── GlobalUsings.cs ├── IntegrationTests │ ├── Graphs │ │ ├── Child1Entity.cs │ │ ├── Child1GraphType.cs │ │ ├── Child2Entity.cs │ │ ├── Child2GraphType.cs │ │ ├── ChildEntity.cs │ │ ├── ChildGraphType.cs │ │ ├── CustomTypeEntity.cs │ │ ├── CustomTypeGraphType.cs │ │ ├── DateEntity.cs │ │ ├── DateEntityGraphType.cs │ │ ├── EnumEntity.cs │ │ ├── EnumEntityGraphType.cs │ │ ├── Filtering │ │ │ ├── FilterChildEntity.cs │ │ │ ├── FilterChildGraphType.cs │ │ │ ├── FilterParentEntity.cs │ │ │ └── FilterParentGraphType.cs │ │ ├── IncludeNonQueryable │ │ │ ├── IncludeNonQueryableA.cs │ │ │ ├── IncludeNonQueryableAGraphType.cs │ │ │ ├── IncludeNonQueryableB.cs │ │ │ └── IncludeNonQueryableBGraphType.cs │ │ ├── Inheritance │ │ │ ├── DerivedChildEntity.cs │ │ │ ├── DerivedChildGraphType.cs │ │ │ ├── DerivedEntity.cs │ │ │ ├── DerivedGraphType.cs │ │ │ ├── DerivedWithNavigationEntity.cs │ │ │ ├── DerivedWithNavigationGraphType.cs │ │ │ ├── InheritedEntity.cs │ │ │ └── InterfaceGraphType.cs │ │ ├── Levels │ │ │ ├── Level1Entity.cs │ │ │ ├── Level1GraphType.cs │ │ │ ├── Level2Entity.cs │ │ │ ├── Level2GraphType.cs │ │ │ ├── Level3Entity.cs │ │ │ └── Level3GraphType.cs │ │ ├── ManyToMany │ │ │ ├── ManyToManyLeftEntity.cs │ │ │ ├── ManyToManyLeftGraphType.cs │ │ │ ├── ManyToManyMiddleEntity.cs │ │ │ ├── ManyToManyRightEntity.cs │ │ │ ├── ManyToManyRightGraphType.cs │ │ │ ├── ManyToManyShadowLeftEntity.cs │ │ │ └── ManyToManyShadowRightEntity.cs │ │ ├── MisNamed │ │ │ ├── WithMisNamedQueryChildEntity.cs │ │ │ ├── WithMisNamedQueryChildGraphType.cs │ │ │ ├── WithMisNamedQueryParentEntity.cs │ │ │ └── WithMisNamedQueryParentGraphType.cs │ │ ├── NamedId │ │ │ ├── NamedIdEntity.cs │ │ │ └── NamedIdGraphType.cs │ │ ├── Nullable │ │ │ ├── WithNullableEntity.cs │ │ │ └── WithNullableGraphType.cs │ │ ├── Owned │ │ │ ├── OwnedChild.cs │ │ │ ├── OwnedChildGraphType.cs │ │ │ ├── OwnedParent.cs │ │ │ └── OwnedParentGraphType.cs │ │ ├── ParentEntity.cs │ │ ├── ParentEntityView.cs │ │ ├── ParentEntityViewGraphType.cs │ │ ├── ParentGraphType.cs │ │ ├── SkipLevelGraph.cs │ │ ├── StringEntity.cs │ │ ├── StringEntityGraphType.cs │ │ ├── TimeEntity.cs │ │ ├── TimeEntityGraphType.cs │ │ ├── WithManyChildrenEntity.cs │ │ └── WithManyChildrenGraphType.cs │ ├── IntegrationTests.Child_filtered.verified.txt │ ├── IntegrationTests.Child_parent.verified.txt │ ├── IntegrationTests.Child_parent_with_alias.verified.txt │ ├── IntegrationTests.Connection_first_page.verified.txt │ ├── IntegrationTests.Connection_nested.verified.txt │ ├── IntegrationTests.Connection_nested_OmitQueryArguments.verified.txt │ ├── IntegrationTests.Connection_page_back.verified.txt │ ├── IntegrationTests.Connection_parent_child_Filtered.verified.txt │ ├── IntegrationTests.CustomType.verified.txt │ ├── IntegrationTests.Explicit_Null.verified.txt │ ├── IntegrationTests.FirstNullable_Found.verified.txt │ ├── IntegrationTests.FirstNullable_NotFound.verified.txt │ ├── IntegrationTests.FirstParent_Child.verified.txt │ ├── IntegrationTests.FirstParent_Child_WithFragment.verified.txt │ ├── IntegrationTests.FirstParent_Child_mutation.verified.txt │ ├── IntegrationTests.First_Found.verified.txt │ ├── IntegrationTests.First_Found_Large_Text_NoAsync.verified.txt │ ├── IntegrationTests.First_Found_NoTracking.verified.txt │ ├── IntegrationTests.First_IdOnly.verified.txt │ ├── IntegrationTests.First_NoArgs.verified.txt │ ├── IntegrationTests.First_NotFound.verified.txt │ ├── IntegrationTests.Id.verified.txt │ ├── IntegrationTests.Id_multiple.verified.txt │ ├── IntegrationTests.In.verified.txt │ ├── IntegrationTests.In_multiple.verified.txt │ ├── IntegrationTests.Like.verified.txt │ ├── IntegrationTests.LogQuery.verified.txt │ ├── IntegrationTests.ManyToManyLeftWhereAndInclude.verified.txt │ ├── IntegrationTests.ManyToManyLeftWhereAndInclude_notEqual.verified.txt │ ├── IntegrationTests.ManyToManyRightWhereAndInclude.verified.txt │ ├── IntegrationTests.ManyToManyRightWhereAndInclude_notEqual.verified.txt │ ├── IntegrationTests.Many_children.verified.txt │ ├── IntegrationTests.Multiple_nested.verified.txt │ ├── IntegrationTests.Multiple_nested_Filtered.verified.txt │ ├── IntegrationTests.NamedId.verified.txt │ ├── IntegrationTests.Null_on_nested.verified.txt │ ├── IntegrationTests.Null_on_nested_notEqual.verified.txt │ ├── IntegrationTests.OrderBy.verified.txt │ ├── IntegrationTests.OrderByDescending.verified.txt │ ├── IntegrationTests.OrderByNested.verified.txt │ ├── IntegrationTests.OrderByNestedNullable.verified.txt │ ├── IntegrationTests.OrderByNullable.verified.txt │ ├── IntegrationTests.Owned.verified.txt │ ├── IntegrationTests.Parent_child_with_id.verified.txt │ ├── IntegrationTests.Parent_with_id_child_with_id.verified.txt │ ├── IntegrationTests.Query_Cyclic.verified.txt │ ├── IntegrationTests.Query_Large_Text_NoAsync.verified.txt │ ├── IntegrationTests.Query_NoTracking.verified.txt │ ├── IntegrationTests.RootList_filtered.verified.txt │ ├── IntegrationTests.Root_connectionFiltered.verified.txt │ ├── IntegrationTests.SchemaPrint.verified.txt │ ├── IntegrationTests.SingleNullable_Found.verified.txt │ ├── IntegrationTests.SingleNullable_NotFound.verified.txt │ ├── IntegrationTests.SingleParent_Child.verified.txt │ ├── IntegrationTests.SingleParent_Child_WithFragment.verified.txt │ ├── IntegrationTests.SingleParent_Child_mutation.verified.txt │ ├── IntegrationTests.Single_Found.verified.txt │ ├── IntegrationTests.Single_Found_Large_Text_NoAsync.verified.txt │ ├── IntegrationTests.Single_Found_NoTracking.verified.txt │ ├── IntegrationTests.Single_IdOnly.verified.txt │ ├── IntegrationTests.Single_NoArgs.verified.txt │ ├── IntegrationTests.Single_NotFound.verified.txt │ ├── IntegrationTests.Skip.verified.txt │ ├── IntegrationTests.SkipNoOrder.verified.txt │ ├── IntegrationTests.Take.verified.txt │ ├── IntegrationTests.TakeNoOrder.verified.txt │ ├── IntegrationTests.Where.verified.txt │ ├── IntegrationTests.Where_date.verified.txt │ ├── IntegrationTests.Where_date_notEqual.verified.txt │ ├── IntegrationTests.Where_default_comparison.verified.txt │ ├── IntegrationTests.Where_enum.verified.txt │ ├── IntegrationTests.Where_enum_in.verified.txt │ ├── IntegrationTests.Where_enum_notEqual.verified.txt │ ├── IntegrationTests.Where_multiple.verified.txt │ ├── IntegrationTests.Where_notEqual.verified.txt │ ├── IntegrationTests.Where_null_comparison_value.verified.txt │ ├── IntegrationTests.Where_null_comparison_value_notEqual.verified.txt │ ├── IntegrationTests.Where_string_contains.verified.txt │ ├── IntegrationTests.Where_string_contains_diffCase.verified.txt │ ├── IntegrationTests.Where_string_notEqual.verified.txt │ ├── IntegrationTests.Where_string_notEqual_diffCase.verified.txt │ ├── IntegrationTests.Where_time.verified.txt │ ├── IntegrationTests.Where_time_notEqual.verified.txt │ ├── IntegrationTests.Where_with_nullable_properties1.verified.txt │ ├── IntegrationTests.Where_with_nullable_properties1_NotEqual.verified.txt │ ├── IntegrationTests.Where_with_nullable_properties2.verified.txt │ ├── IntegrationTests.Where_with_nullable_properties2_notEqual.verified.txt │ ├── IntegrationTests.Where_with_variable.verified.txt │ ├── IntegrationTests.Where_with_variable_notEqual.verified.txt │ ├── IntegrationTests.With_null_navigation_property.verified.txt │ ├── IntegrationTests.With_null_navigation_property_notEqual.verified.txt │ ├── IntegrationTests.cs │ ├── IntegrationTests_filtered.cs │ ├── Mutation.cs │ ├── MyDataContext.cs │ ├── Query.cs │ ├── QueryExecutor.cs │ └── Schema.cs ├── Mapping │ ├── MappingChild.cs │ ├── MappingChildGraphType.cs │ ├── MappingContext.cs │ ├── MappingParent.cs │ ├── MappingParentGraphType.cs │ ├── MappingQuery.cs │ ├── MappingSchema.cs │ ├── MappingTests.NavigationProperty.verified.txt │ ├── MappingTests.PropertyToObject.verified.txt │ ├── MappingTests.Resolve.verified.txt │ ├── MappingTests.SchemaPrint.verified.txt │ └── MappingTests.cs ├── ModuleInitializer.cs ├── MultiContextTests │ ├── DbContext1.cs │ ├── DbContext2.cs │ ├── Graphs │ │ ├── Entity1.cs │ │ ├── Entity1GraphType.cs │ │ ├── Entity2.cs │ │ └── Entity2GraphType.cs │ ├── MultiContextQuery.cs │ ├── MultiContextSchema.cs │ ├── MultiContextTests.Run.verified.txt │ └── MultiContextTests.cs ├── PropertyCacheTests.cs ├── ResultSerializer.cs ├── Tests.csproj └── TypeConverterTests.cs ├── appveyor.yml ├── global.json ├── icon.png ├── key.snk ├── mdsnippets.json └── nuget.config /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | .editorconfig -text 3 | *.sln.DotSettings -text eol=crlf 4 | *.snk binary 5 | 6 | *.verified.txt text eol=lf working-tree-encoding=UTF-8 7 | *.verified.xml text eol=lf working-tree-encoding=UTF-8 8 | *.verified.json text eol=lf working-tree-encoding=UTF-8 9 | 10 | .editorconfig text eol=lf working-tree-encoding=UTF-8 11 | Shared.sln.DotSettings text eol=lf working-tree-encoding=UTF-8 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/src" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/merge-dependabot.yml: -------------------------------------------------------------------------------- 1 | name: merge-dependabot 2 | on: 3 | pull_request: 4 | jobs: 5 | automerge: 6 | runs-on: ubuntu-latest 7 | if: github.actor == 'dependabot[bot]' 8 | steps: 9 | - name: Dependabot Auto Merge 10 | uses: ahmadnassri/action-dependabot-auto-merge@v2.6.6 11 | with: 12 | target: minor 13 | github-token: ${{ secrets.dependabot }} 14 | command: squash and merge -------------------------------------------------------------------------------- /.github/workflows/on-push-do-docs.yml: -------------------------------------------------------------------------------- 1 | name: on-push-do-docs 2 | on: 3 | push: 4 | jobs: 5 | docs: 6 | runs-on: windows-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - name: Run MarkdownSnippets 10 | run: | 11 | dotnet tool install --global MarkdownSnippets.Tool 12 | mdsnippets ${GITHUB_WORKSPACE} 13 | shell: bash 14 | - name: Push changes 15 | run: | 16 | git config --local user.email "action@github.com" 17 | git config --local user.name "GitHub Action" 18 | git commit -m "Docs changes" -a || echo "nothing to commit" 19 | remote="https://${GITHUB_ACTOR}:${{secrets.GITHUB_TOKEN}}@github.com/${GITHUB_REPOSITORY}.git" 20 | branch="${GITHUB_REF:11}" 21 | git push "${remote}" ${branch} || echo "nothing to push" 22 | shell: bash -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | bin/ 4 | obj/ 5 | .vs/ 6 | *.DotSettings.user 7 | .idea/ 8 | *.received.* 9 | nugets/ -------------------------------------------------------------------------------- /docs/mdsource/filters.source.md: -------------------------------------------------------------------------------- 1 | # Filters 2 | 3 | Sometimes, in the context of constructing an EF query, it is not possible to know if any given item should be returned in the results. For example when performing authorization where the rules rules are pulled from a different system, and that information does not exist in the database. 4 | 5 | `Filters` allows a custom function to be executed after the EF query execution and determine if any given node should be included in the result. 6 | 7 | Notes: 8 | 9 | * When evaluated on nodes of a collection, excluded nodes will be removed from collection. 10 | * When evaluated on a property node, the value will be replaced with null. 11 | * When doing paging or counts, there is currently no smarts that adjust counts or pages sizes when items are excluded. If this is required submit a PR that adds this feature, or don't mix filters with paging. 12 | * The filter is passed the current [User Context](https://graphql-dotnet.github.io/docs/getting-started/user-context) and the node item instance. 13 | * Filters will not be executed on null item instance. 14 | * A [Type.IsAssignableFrom](https://docs.microsoft.com/en-us/dotnet/api/system.type.isassignablefrom) check will be performed to determine if an item instance should be filtered based on the ``. 15 | 16 | 17 | ### Signature: 18 | 19 | snippet: FiltersSignature 20 | 21 | 22 | ### Usage: 23 | 24 | snippet: add-filter -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Simon Cropp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # GraphQL.EntityFramework 9 | 10 | [![Build status](https://ci.appveyor.com/api/projects/status/x554cp7clu8yh2yy/branch/main?svg=true)](https://ci.appveyor.com/project/SimonCropp/graphql-entityframework) 11 | [![NuGet Status](https://img.shields.io/nuget/v/GraphQL.EntityFramework.svg)](https://www.nuget.org/packages/GraphQL.EntityFramework/) 12 | 13 | Add [EntityFramework Core IQueryable](https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbset-1.system-linq-iqueryable-provider) support to [GraphQL](https://github.com/graphql-dotnet/graphql-dotnet) 14 | 15 | 16 | **See [Milestones](../../milestones?state=closed) for release notes.** 17 | 18 | 19 | ### Powered by 20 | 21 | [![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport) 22 | 23 | 24 | ## NuGet package 25 | 26 | https://nuget.org/packages/GraphQL.EntityFramework/ 27 | 28 | 29 | ## Documentation 30 | 31 | * [Configuration](/docs/configuration.md) 32 | * [Defining Graphs](/docs/defining-graphs.md) 33 | * [Query Usage](/docs/query-usage.md) 34 | * [Filters](/docs/filters.md) 35 | 36 | 37 | ## Icon 38 | 39 | [Memory](https://thenounproject.com/term/database/1631008/) designed by H Alberto Gongora from [The Noun Project](https://thenounproject.com) 40 | -------------------------------------------------------------------------------- /readme.source.md: -------------------------------------------------------------------------------- 1 | # GraphQL.EntityFramework 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/x554cp7clu8yh2yy/branch/main?svg=true)](https://ci.appveyor.com/project/SimonCropp/graphql-entityframework) 4 | [![NuGet Status](https://img.shields.io/nuget/v/GraphQL.EntityFramework.svg)](https://www.nuget.org/packages/GraphQL.EntityFramework/) 5 | 6 | Add [EntityFramework Core IQueryable](https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbset-1.system-linq-iqueryable-provider) support to [GraphQL](https://github.com/graphql-dotnet/graphql-dotnet) 7 | 8 | 9 | **See [Milestones](../../milestones?state=closed) for release notes.** 10 | 11 | 12 | ### Powered by 13 | 14 | [![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport) 15 | 16 | 17 | ## NuGet package 18 | 19 | https://nuget.org/packages/GraphQL.EntityFramework/ 20 | 21 | 22 | ## Documentation 23 | 24 | * [Configuration](/docs/configuration.md) 25 | * [Defining Graphs](/docs/defining-graphs.md) 26 | * [Query Usage](/docs/query-usage.md) 27 | * [Filters](/docs/filters.md) 28 | 29 | 30 | ## Icon 31 | 32 | [Memory](https://thenounproject.com/term/database/1631008/) designed by H Alberto Gongora from [The Noun Project](https://thenounproject.com) -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CS1591;NU5104;CS1573;CS9107;NU1608;NU1109 5 | 31.0.3 6 | preview 7 | 1.0.0 8 | EntityFrameworkCore, EntityFramework, GraphQL 9 | true 10 | true 11 | true 12 | true 13 | true 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | ..\Shared.sln.DotSettings 3 | True 4 | True 5 | 1 6 | -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Compress.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public static class Compress 4 | { 5 | public static string Query(string query) 6 | { 7 | Guard.AgainstWhiteSpace(nameof(query), query); 8 | query = Regex.Replace(query, @"\s+", " "); 9 | return Regex.Replace(query, @"\s*(\[|\]|\{|\}|\(|\)|:|\,)\s*", "$1"); 10 | } 11 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/EfDocumentExecuter.cs: -------------------------------------------------------------------------------- 1 | using ExecutionContext = GraphQL.Execution.ExecutionContext; 2 | 3 | namespace GraphQL.EntityFramework; 4 | 5 | public class EfDocumentExecuter : 6 | DocumentExecuter 7 | { 8 | protected override IExecutionStrategy SelectExecutionStrategy(ExecutionContext context) 9 | { 10 | if (context.Operation.Operation == OperationType.Query) 11 | { 12 | return new SerialExecutionStrategy(); 13 | } 14 | 15 | return base.SelectExecutionStrategy(context); 16 | } 17 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Filters/ResolveFilters.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public delegate Filters? ResolveFilters(object userContext) 4 | where TDbContext : DbContext; -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System.Security.Claims; 2 | global using Microsoft.AspNetCore.Http; 3 | global using Microsoft.EntityFrameworkCore.Metadata; 4 | global using Microsoft.Extensions.DependencyInjection; -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/EfGraphQLService_Navigation.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | partial class EfGraphQLService 4 | where TDbContext : DbContext 5 | { 6 | public FieldBuilder AddNavigationField( 7 | ComplexGraphType graph, 8 | string name, 9 | Func, TReturn?>? resolve = null, 10 | Type? graphType = null, 11 | IEnumerable? includeNames = null) 12 | where TReturn : class 13 | { 14 | Guard.AgainstWhiteSpace(nameof(name), name); 15 | 16 | graphType ??= GraphTypeFinder.FindGraphType(); 17 | 18 | var field = new FieldType 19 | { 20 | Name = name, 21 | Type = graphType 22 | }; 23 | IncludeAppender.SetIncludeMetadata(field, name, includeNames); 24 | 25 | if (resolve is not null) 26 | { 27 | field.Resolver = new FuncFieldResolver( 28 | async context => 29 | { 30 | var fieldContext = BuildContext(context); 31 | 32 | TReturn? result; 33 | try 34 | { 35 | result = resolve(fieldContext); 36 | } 37 | catch (Exception exception) 38 | { 39 | throw new( 40 | $""" 41 | Failed to execute navigation resolve for field `{name}` 42 | GraphType: {graphType.FullName} 43 | TSource: {typeof(TSource).FullName} 44 | TReturn: {typeof(TReturn).FullName} 45 | """, 46 | exception); 47 | } 48 | 49 | if (fieldContext.Filters == null || 50 | await fieldContext.Filters.ShouldInclude(context.UserContext, fieldContext.DbContext, context.User, result)) 51 | { 52 | return result; 53 | } 54 | 55 | return null; 56 | }); 57 | } 58 | 59 | graph.AddField(field); 60 | return new FieldBuilderEx(field); 61 | } 62 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/EfGraphQLService_NavigationList.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | partial class EfGraphQLService 4 | where TDbContext : DbContext 5 | { 6 | public FieldBuilder AddNavigationListField( 7 | ComplexGraphType graph, 8 | string name, 9 | Func, IEnumerable>? resolve = null, 10 | Type? itemGraphType = null, 11 | IEnumerable? includeNames = null, 12 | bool omitQueryArguments = false) 13 | where TReturn : class 14 | { 15 | Guard.AgainstWhiteSpace(nameof(name), name); 16 | 17 | var hasId = keyNames.ContainsKey(typeof(TReturn)); 18 | var field = new FieldType 19 | { 20 | Name = name, 21 | Type = MakeListGraphType(itemGraphType), 22 | Arguments = ArgumentAppender.GetQueryArguments(hasId, true, false), 23 | }; 24 | IncludeAppender.SetIncludeMetadata(field, name, includeNames); 25 | 26 | if (resolve is not null) 27 | { 28 | field.Resolver = new FuncFieldResolver>(async context => 29 | { 30 | var fieldContext = BuildContext(context); 31 | var result = resolve(fieldContext); 32 | 33 | if (result is IQueryable) 34 | { 35 | throw new("This API expects the resolver to return a IEnumerable, not an IQueryable. Instead use AddQueryField."); 36 | } 37 | 38 | result = result.ApplyGraphQlArguments(hasId, context, omitQueryArguments); 39 | if (fieldContext.Filters == null) 40 | { 41 | return result; 42 | } 43 | 44 | return await fieldContext.Filters.ApplyFilter(result, context.UserContext, fieldContext.DbContext, context.User); 45 | }); 46 | } 47 | 48 | graph.AddField(field); 49 | return new FieldBuilderEx(field); 50 | } 51 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/FieldBuilderEx.cs: -------------------------------------------------------------------------------- 1 | class FieldBuilderEx(FieldType fieldType) : 2 | FieldBuilder(fieldType) 3 | { 4 | public override FieldBuilder Resolve(IFieldResolver? resolver) => 5 | throw new("The resolve has already been configured"); 6 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/IEfGraphQLService.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public partial interface IEfGraphQLService 4 | where TDbContext : DbContext 5 | { 6 | TDbContext ResolveDbContext(IResolveFieldContext context); 7 | 8 | IQueryable AddIncludes(IQueryable query, IResolveFieldContext context) 9 | where TItem : class; 10 | 11 | public IReadOnlyDictionary> Navigations { get; } 12 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/IEfGraphQLService_Navigation.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | //Navigation fields will always be on a typed graph. so use ComplexGraphType not IComplexGraphType 4 | public partial interface IEfGraphQLService 5 | { 6 | FieldBuilder AddNavigationField(ComplexGraphType graph, 7 | string name, 8 | Func, TReturn?>? resolve = null, 9 | Type? graphType = null, 10 | IEnumerable? includeNames = null) 11 | where TReturn : class; 12 | 13 | FieldBuilder AddNavigationListField( 14 | ComplexGraphType graph, 15 | string name, 16 | Func, IEnumerable>? resolve = null, 17 | Type? itemGraphType = null, 18 | IEnumerable? includeNames = null, 19 | bool omitQueryArguments = false) 20 | where TReturn : class; 21 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/IEfGraphQLService_NavigationConnection.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | //Navigation fields will always be on a typed graph. so use ComplexGraphType not IComplexGraphType 4 | public partial interface IEfGraphQLService 5 | { 6 | ConnectionBuilder AddNavigationConnectionField( 7 | ComplexGraphType graph, 8 | string name, 9 | Func, IEnumerable>? resolve = null, 10 | Type? itemGraphType = null, 11 | IEnumerable? includeNames = null, 12 | bool omitQueryArguments = false) 13 | where TReturn : class; 14 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/IEfGraphQLService_Queryable.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public partial interface IEfGraphQLService 4 | { 5 | FieldBuilder AddQueryField( 6 | IComplexGraphType graph, 7 | string name, 8 | Func, IQueryable>? resolve = null, 9 | Type? itemGraphType = null, 10 | bool omitQueryArguments = false) 11 | where TReturn : class; 12 | FieldBuilder AddQueryField( 13 | IComplexGraphType graph, 14 | string name, 15 | Func, Task>>? resolve = null, 16 | Type? itemGraphType = null, 17 | bool omitQueryArguments = false) 18 | where TReturn : class; 19 | 20 | FieldBuilder AddQueryField( 21 | IComplexGraphType graph, 22 | string name, 23 | Func, IQueryable>? resolve = null, 24 | Type? itemGraphType = null, 25 | bool omitQueryArguments = false) 26 | where TReturn : class; 27 | 28 | FieldBuilder AddQueryField( 29 | IComplexGraphType graph, 30 | string name, 31 | Func, Task>>? resolve = null, 32 | Type? itemGraphType = null, 33 | bool omitQueryArguments = false) 34 | where TReturn : class; 35 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/IEfGraphQLService_QueryableConnection.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public partial interface IEfGraphQLService 4 | { 5 | ConnectionBuilder AddQueryConnectionField( 6 | IComplexGraphType graph, 7 | string name, 8 | Func, IOrderedQueryable>? resolve = null, 9 | Type? itemGraphType = null, 10 | bool omitQueryArguments = false) 11 | where TReturn : class; 12 | 13 | ConnectionBuilder AddQueryConnectionField( 14 | IComplexGraphType graph, 15 | string name, 16 | Func, Task>>? resolve = null, 17 | Type? itemGraphType = null, 18 | bool omitQueryArguments = false) 19 | where TReturn : class; 20 | 21 | ConnectionBuilder AddQueryConnectionField( 22 | IComplexGraphType graph, 23 | string name, 24 | Func, IOrderedQueryable>? resolve = null, 25 | Type? itemGraphType = null, 26 | bool omitQueryArguments = false) 27 | where TReturn : class; 28 | 29 | ConnectionBuilder AddQueryConnectionField( 30 | IComplexGraphType graph, 31 | string name, 32 | Func, Task>>? resolve = null, 33 | Type? itemGraphType = null, 34 | bool omitQueryArguments = false) 35 | where TReturn : class; 36 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/ResolveDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public delegate TDbContext ResolveDbContext(object userContext, IServiceProvider? requestServices) 4 | where TDbContext : DbContext; -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/ResolveEfFieldContext.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public class ResolveEfFieldContext : 4 | ResolveFieldContext 5 | where TDbContext : DbContext 6 | { 7 | public TDbContext DbContext { get; set; } = null!; 8 | public Filters? Filters { get; set; } 9 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphApi/SingleEntityNotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public class SingleEntityNotFoundException : 4 | Exception 5 | { 6 | public override string Message => "Not found"; 7 | } 8 | public class FirstEntityNotFoundException : 9 | Exception 10 | { 11 | public override string Message => "Not found"; 12 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphQL.EntityFramework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | Add EntityFramework Core IQueryable support to GraphQL 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphQlExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public static class GraphQlExtensions 4 | { 5 | #region ExecuteWithErrorCheck 6 | 7 | public static async Task ExecuteWithErrorCheck( 8 | this IDocumentExecuter executer, 9 | ExecutionOptions options) 10 | { 11 | var executionResult = await executer.ExecuteAsync(options); 12 | 13 | options.ThrowOnUnhandledException = true; 14 | var errors = executionResult.Errors; 15 | if (errors is { Count: > 0 }) 16 | { 17 | if (errors.Count == 1) 18 | { 19 | throw errors.First(); 20 | } 21 | 22 | throw new AggregateException(errors); 23 | } 24 | 25 | return executionResult; 26 | } 27 | 28 | #endregion 29 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/GraphTypeFinder.cs: -------------------------------------------------------------------------------- 1 | static class GraphTypeFinder 2 | { 3 | public static Type FindGraphType(bool isNullable = false) 4 | where TReturn : class 5 | { 6 | var type = typeof(TReturn); 7 | return FindGraphType(type, isNullable); 8 | } 9 | 10 | public static Type FindGraphType(Type type, bool isNullable = false) => 11 | type.GetGraphTypeFromType(isNullable, TypeMappingMode.OutputType); 12 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Guard.cs: -------------------------------------------------------------------------------- 1 | static class Guard 2 | { 3 | public static void AgainstWhiteSpace(string argumentName, string value) 4 | { 5 | if (string.IsNullOrWhiteSpace(value)) 6 | { 7 | throw new ArgumentNullException(argumentName); 8 | } 9 | } 10 | 11 | public static void AgainstNegative(string argumentName, int value) 12 | { 13 | if (value < 0) 14 | { 15 | throw new ArgumentNullException(argumentName); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/HttpContextCapture.cs: -------------------------------------------------------------------------------- 1 | class HttpContextCapture(IHttpContextAccessor httpContextAccessor) 2 | { 3 | public IHttpContextAccessor HttpContextAccessor { get; } = httpContextAccessor; 4 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/InternalsVisibleTo.cs: -------------------------------------------------------------------------------- 1 | [assembly: InternalsVisibleTo("Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001006944a8a4c5de92d68196a123157958d8f212381e1e21a772626e9c86cf8032f457f5b3d669045c4183f4d8dc89be3ae953ccba62b7522b2633156d0c886597509d50de36ce53cac9eebae9146e061bd6933499c740f56770c9ca3052f51de162791f86ea316ae4e3345c58b53e5743a3301359820655979aafefb40384c585c5")] -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/KeyNameExtractor.cs: -------------------------------------------------------------------------------- 1 | static class KeyNameExtractor 2 | { 3 | public static IReadOnlyDictionary> GetKeyNames(this IModel model) 4 | { 5 | var keyNames = new Dictionary>(); 6 | foreach (var entity in model.GetEntityTypes()) 7 | { 8 | var clrType = entity.ClrType; 9 | 10 | // join entities ClrTypes are dictionaries 11 | if (clrType.Assembly.FullName!.StartsWith("System")) 12 | { 13 | continue; 14 | } 15 | 16 | var primaryKey = entity.FindPrimaryKey(); 17 | //This can happen for views 18 | if (primaryKey is null) 19 | { 20 | continue; 21 | } 22 | 23 | if (entity.IsOwned()) 24 | { 25 | continue; 26 | } 27 | 28 | var names = primaryKey.Properties.Select(_ => _.Name).ToList(); 29 | keyNames.Add(clrType, names); 30 | } 31 | 32 | return keyNames; 33 | } 34 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Mapping/GetGraphException.cs: -------------------------------------------------------------------------------- 1 | class GetGraphException(string message) : 2 | Exception(message); -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Mapping/SimpleFieldResolver.cs: -------------------------------------------------------------------------------- 1 | class SimpleFieldResolver(Func func) : 2 | IFieldResolver 3 | { 4 | public ValueTask ResolveAsync(IResolveFieldContext context) 5 | { 6 | var source = (TSource) context.Source!; 7 | return ValueTask.FromResult(func(source)); 8 | } 9 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Navigation.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | [DebuggerDisplay("Name = {Name}, Type = {Type}")] 4 | public record Navigation 5 | ( 6 | string Name, 7 | Type Type, 8 | bool IsNullable, 9 | bool IsCollection 10 | ); -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/NavigationReader.cs: -------------------------------------------------------------------------------- 1 | static class NavigationReader 2 | { 3 | public static IReadOnlyDictionary> GetNavigationProperties(IModel model) 4 | { 5 | var dictionary = new Dictionary>(); 6 | foreach (var property in model.GetEntityTypes()) 7 | { 8 | if (!dictionary.ContainsKey(property.ClrType)) 9 | { 10 | dictionary[property.ClrType] = GetNavigations(property); 11 | } 12 | } 13 | 14 | return dictionary; 15 | } 16 | 17 | static IReadOnlyList GetNavigations(IEntityType entity) 18 | { 19 | var navigations = entity.GetNavigations() 20 | .Cast().Concat(entity.GetSkipNavigations()); 21 | return navigations 22 | .Select( 23 | _ => 24 | { 25 | var (itemType, isCollection) = GetNavigationType(_); 26 | return new Navigation(_.Name, itemType, _.PropertyInfo!.IsNullable(), isCollection); 27 | }) 28 | .ToList(); 29 | } 30 | 31 | static (Type itemType, bool isCollection) GetNavigationType(INavigationBase navigation) 32 | { 33 | var navigationType = navigation.ClrType; 34 | if (navigationType.TryGetCollectionType(out var collectionGenericType)) 35 | { 36 | return (collectionGenericType, true); 37 | } 38 | 39 | return (navigationType, false); 40 | } 41 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/PropertyAccess/Property.cs: -------------------------------------------------------------------------------- 1 | record Property( 2 | Expression Left, 3 | Expression> Lambda, 4 | ParameterExpression SourceParameter, 5 | Func Func, 6 | Type PropertyType, 7 | MemberInfo Info, 8 | MethodInfo? ListContains); 9 | -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/QueryLogger.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public static class QueryLogger 4 | { 5 | static Action? log; 6 | 7 | public static void Enable(Action log) => 8 | QueryLogger.log = log; 9 | 10 | internal static void Write(IQueryable queryable) => 11 | log?.Invoke(queryable.ToQueryString()); 12 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Testing/ClientQueryExecutor.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Headers; 2 | 3 | namespace GraphQL.EntityFramework.Testing; 4 | 5 | public class ClientQueryExecutor(Func json, string uri = "graphql") 6 | { 7 | public Task ExecutePost(HttpClient client, string query, object? variables = null, Action? headerAction = null) 8 | { 9 | Guard.AgainstWhiteSpace(nameof(query), query); 10 | query = CompressQuery(query); 11 | var body = new 12 | { 13 | query, 14 | variables 15 | }; 16 | var request = new HttpRequestMessage(HttpMethod.Post, uri) 17 | { 18 | Content = new StringContent(ToJson(body), Encoding.UTF8, "application/json") 19 | }; 20 | headerAction?.Invoke(request.Headers); 21 | return client.SendAsync(request); 22 | } 23 | 24 | public Task ExecuteGet(HttpClient client, string query, object? variables = null, Action? headerAction = null) 25 | { 26 | Guard.AgainstWhiteSpace(nameof(query), query); 27 | var compressed = CompressQuery(query); 28 | var variablesString = ToJson(variables); 29 | var getUri = $"{uri}?query={compressed}&variables={variablesString}"; 30 | var request = new HttpRequestMessage(HttpMethod.Get, getUri); 31 | headerAction?.Invoke(request.Headers); 32 | return client.SendAsync(request); 33 | } 34 | 35 | string ToJson(object? target) 36 | { 37 | if (target is null) 38 | { 39 | return string.Empty; 40 | } 41 | 42 | return json(target); 43 | } 44 | 45 | static string CompressQuery(string query) => 46 | Compress.Query(query); 47 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/ArgumentProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public static partial class ArgumentProcessor 4 | { 5 | static void EnsureOrderForTake(bool order, IResolveFieldContext context) 6 | { 7 | if (order) 8 | { 9 | return; 10 | } 11 | 12 | context.Errors.Add(new($"If `take` is used an `orderBy` must be specified. Field: {context.FieldDefinition.Name}")); 13 | } 14 | 15 | static void EnsureOrderForSkip(bool order, IResolveFieldContext context) 16 | { 17 | if (order) 18 | { 19 | return; 20 | } 21 | 22 | context.Errors.Add(new($"If `skip` is used an `orderBy` must be specified. Field: {context.FieldDefinition.Name}")); 23 | } 24 | 25 | static string GetKeyName(IReadOnlyList keyNames) 26 | { 27 | if (keyNames.Count > 1) 28 | { 29 | throw new("Only one id field is currently supported"); 30 | } 31 | 32 | return keyNames[0]; 33 | } 34 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/ExpressionCache.cs: -------------------------------------------------------------------------------- 1 | static class ExpressionCache 2 | { 3 | public static ConstantExpression NegativeOne = Expression.Constant(-1); 4 | public static ConstantExpression Null = Expression.Constant(null, typeof(object)); 5 | public static ConstantExpression EfFunction = Expression.Constant(EF.Functions); 6 | public static ParameterExpression StringParam = Expression.Parameter(typeof(string)); 7 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/Graphs/Comparison.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public enum Comparison 4 | { 5 | // Both 6 | Equal, 7 | NotEqual, 8 | In, 9 | NotIn, 10 | 11 | // Object/ List 12 | GreaterThan, 13 | GreaterThanOrEqual, 14 | LessThan, 15 | LessThanOrEqual, 16 | 17 | // String 18 | StartsWith, 19 | EndsWith, 20 | Contains, 21 | Like 22 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/Graphs/ComparisonGraph.cs: -------------------------------------------------------------------------------- 1 | class ComparisonGraph : 2 | EnumerationGraphType 3 | { 4 | public ComparisonGraph() 5 | { 6 | Name = nameof(Comparison); 7 | Add("contains", Comparison.Contains); 8 | Add("endsWith", Comparison.EndsWith); 9 | Add("equal", Comparison.Equal); 10 | Add("notEqual", Comparison.NotEqual); 11 | Add("greaterThan", Comparison.GreaterThan); 12 | Add("greaterThanOrEqual", Comparison.GreaterThanOrEqual); 13 | Add("notIn", Comparison.NotIn, "Negation Property used with the 'in' comparison should be used in place of this"); 14 | Add("in", Comparison.In); 15 | Add("lessThan", Comparison.LessThan); 16 | Add("lessThanOrEqual", Comparison.LessThanOrEqual); 17 | Add("like", Comparison.Like); 18 | Add("startsWith", Comparison.StartsWith); 19 | } 20 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/Graphs/Connector.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public enum Connector 4 | { 5 | And, 6 | Or 7 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/Graphs/ConnectorGraph.cs: -------------------------------------------------------------------------------- 1 | class ConnectorGraph : 2 | EnumerationGraphType 3 | { 4 | public ConnectorGraph() 5 | { 6 | Name = nameof(Connector); 7 | Add("and", Connector.And); 8 | Add("or", Connector.Or); 9 | } 10 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/Graphs/OrderBy.cs: -------------------------------------------------------------------------------- 1 | class OrderBy 2 | { 3 | public string Path { get; set; } = null!; 4 | public bool Descending { get; set; } 5 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/Graphs/OrderByGraph.cs: -------------------------------------------------------------------------------- 1 | class OrderByGraph : 2 | InputObjectGraphType 3 | { 4 | public OrderByGraph() 5 | { 6 | Name = nameof(OrderBy); 7 | Field(_ => _.Path); 8 | Field(_ => _.Descending, true); 9 | } 10 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/Graphs/WhereExpression.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public class WhereExpression 4 | { 5 | public string Path { get; set; } = string.Empty; 6 | public Comparison Comparison { get; set; } = Comparison.Equal; 7 | public string[]? Value { get; set; } 8 | public bool Negate { get; set; } 9 | public Connector Connector { get; set; } = Connector.And; 10 | public WhereExpression[]? GroupedExpressions { get; set; } 11 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/Graphs/WhereExpressionGraph.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.EntityFramework; 2 | 3 | public class WhereExpressionGraph : 4 | InputObjectGraphType 5 | { 6 | public WhereExpressionGraph() 7 | { 8 | Name = nameof(WhereExpression); 9 | Field(_ => _.Path, true); 10 | Field("comparison"); 11 | Field(_ => _.Negate, true); 12 | Field(_ => _.Value, true); 13 | Field("connector"); 14 | Field>("GroupedExpressions"); 15 | } 16 | } -------------------------------------------------------------------------------- /src/GraphQL.EntityFramework/Where/WhereValidator.cs: -------------------------------------------------------------------------------- 1 | static class WhereValidator 2 | { 3 | public static void ValidateObject(Type propertyType, Comparison comparison) 4 | { 5 | if (comparison is 6 | Comparison.Contains or 7 | Comparison.StartsWith or 8 | Comparison.EndsWith or 9 | Comparison.Like) 10 | { 11 | throw new($"Cannot perform {comparison} on {propertyType.FullName}."); 12 | } 13 | } 14 | 15 | public static void ValidateSingleObject(Type propertyType, Comparison comparison) 16 | { 17 | ValidateObject(propertyType, comparison); 18 | if (comparison == Comparison.In) 19 | { 20 | throw new($"Cannot perform {comparison} on {propertyType.FullName}."); 21 | } 22 | } 23 | 24 | public static void ValidateString(Comparison comparison) 25 | { 26 | if (comparison is 27 | Comparison.GreaterThan or 28 | Comparison.GreaterThanOrEqual or 29 | Comparison.LessThanOrEqual or 30 | Comparison.LessThan) 31 | { 32 | throw new($"Cannot perform {comparison} on a String."); 33 | } 34 | } 35 | 36 | public static void ValidateSingleString(Comparison comparison) 37 | { 38 | ValidateString(comparison); 39 | if (comparison == Comparison.In) 40 | { 41 | throw new($"Cannot perform {comparison} on a single String."); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using GraphQL.EntityFramework.Testing; 2 | global using GraphQL.Types; 3 | global using GraphQL.Utilities; 4 | global using Microsoft.AspNetCore.Hosting; 5 | global using Microsoft.AspNetCore.TestHost; 6 | global using Microsoft.Extensions.DependencyInjection; 7 | global using Microsoft.Extensions.Logging; 8 | global using Microsoft.Extensions.Logging.Abstractions; -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GraphQlControllerTests.Companies_paging.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "companiesConnection": { 4 | "edges": [ 5 | { 6 | "cursor": "2", 7 | "node": { 8 | "id": 6 9 | } 10 | }, 11 | { 12 | "cursor": "3", 13 | "node": { 14 | "id": 7 15 | } 16 | } 17 | ], 18 | "pageInfo": { 19 | "endCursor": "3", 20 | "hasNextPage": false 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GraphQlControllerTests.Complex_query_result.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "employees": [ 4 | { 5 | "id": 3 6 | }, 7 | { 8 | "id": 5 9 | } 10 | ] 11 | } 12 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GraphQlControllerTests.Employee_summary.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "employeeSummary": [ 4 | { 5 | "companyId": 1, 6 | "averageAge": 28 7 | }, 8 | { 9 | "companyId": 4, 10 | "averageAge": 17 11 | } 12 | ] 13 | } 14 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GraphQlControllerTests.First.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "Cannot query field \u0027companyFirst\u0027 on type \u0027Query\u0027. Did you mean \u0027companies\u0027, \u0027company\u0027, \u0027companyById\u0027, or \u0027companyOrNull\u0027?", 5 | "locations": [ 6 | { 7 | "line": 1, 8 | "column": 16 9 | } 10 | ], 11 | "extensions": { 12 | "code": "FIELDS_ON_CORRECT_TYPE", 13 | "codes": [ 14 | "FIELDS_ON_CORRECT_TYPE" 15 | ], 16 | "number": "5.3.1" 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GraphQlControllerTests.Get.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "companies": [ 4 | { 5 | "id": 1 6 | }, 7 | { 8 | "id": 4 9 | }, 10 | { 11 | "id": 6 12 | }, 13 | { 14 | "id": 7 15 | } 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GraphQlControllerTests.Post.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "companies": [ 4 | { 5 | "id": 1 6 | }, 7 | { 8 | "id": 4 9 | }, 10 | { 11 | "id": 6 12 | }, 13 | { 14 | "id": 7 15 | } 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GraphQlControllerTests.Single.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "company": { 4 | "id": 1 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GraphQlControllerTests.Single_not_found.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: SingleEntityNotFoundException, 3 | Message: Not found 4 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/GraphQlControllerTests.Variable.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "companies": [ 4 | { 5 | "id": 1 6 | } 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /src/SampleWeb.Tests/SampleWeb.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | false 5 | Exe 6 | $(NoWarn);xUnit1051 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/SampleWeb.Tests/SchemaPrint.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | 3 | public class SchemaPrint 4 | { 5 | [Fact] 6 | public async Task Print() 7 | { 8 | var services = new ServiceCollection(); 9 | services.AddSingleton(_ => NullLoggerFactory.Instance); 10 | new Startup().ConfigureServices(services); 11 | 12 | await using var provider = services.BuildServiceProvider(); 13 | var schema = provider.GetRequiredService(); 14 | var print = schema.Print(); 15 | await Verify(print); 16 | } 17 | } -------------------------------------------------------------------------------- /src/SampleWeb/DataContext/Company.cs: -------------------------------------------------------------------------------- 1 | public class Company 2 | { 3 | [DatabaseGenerated(DatabaseGeneratedOption.None)] 4 | public int Id { get; set; } 5 | public string? Content { get; set; } 6 | public List Employees { get; set; } = null!; 7 | } -------------------------------------------------------------------------------- /src/SampleWeb/DataContext/Device.cs: -------------------------------------------------------------------------------- 1 | public class Device 2 | { 3 | public int Id { get; set; } 4 | public string Name { get; set; } = null!; 5 | 6 | [ForeignKey("DeviceId")] 7 | [InverseProperty("Devices")] 8 | public virtual ICollection Employees { get; set; } = []; 9 | } 10 | -------------------------------------------------------------------------------- /src/SampleWeb/DataContext/Employee.cs: -------------------------------------------------------------------------------- 1 | public class Employee 2 | { 3 | [DatabaseGenerated(DatabaseGeneratedOption.None)] 4 | public int Id { get; set; } 5 | public int CompanyId { get; set; } 6 | public Company Company { get; set; } = null!; 7 | public string? Content { get; set; } 8 | public int Age { get; set; } 9 | 10 | [ForeignKey("EmployeeId")] 11 | [InverseProperty("Employees")] 12 | public virtual ICollection Devices { get; set; } = []; 13 | } -------------------------------------------------------------------------------- /src/SampleWeb/DataContext/OrderDetail.cs: -------------------------------------------------------------------------------- 1 | public class OrderDetail 2 | { 3 | [DatabaseGenerated(DatabaseGeneratedOption.None)] 4 | public int Id { get; set; } 5 | 6 | public StreetAddress ShippingAddress { get; set; } = null!; 7 | public StreetAddress BillingAddress { get; set; } = null!; 8 | } -------------------------------------------------------------------------------- /src/SampleWeb/DataContext/SampleDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Diagnostics; 2 | 3 | public class SampleDbContext(DbContextOptions options) : 4 | DbContext(options) 5 | { 6 | public DbSet Employees { get; set; } = null!; 7 | public DbSet Devices { get; set; } = null!; 8 | public DbSet Companies { get; set; } = null!; 9 | public DbSet OrderDetails { get; set; } = null!; 10 | 11 | public static IModel StaticModel { get; } = BuildStaticModel(); 12 | 13 | static IModel BuildStaticModel() 14 | { 15 | var builder = new DbContextOptionsBuilder(); 16 | builder.UseSqlServer("Fake"); 17 | using var dbContext = new SampleDbContext(builder.Options); 18 | return dbContext.Model; 19 | } 20 | 21 | protected override void OnConfiguring(DbContextOptionsBuilder builder) => 22 | builder.ConfigureWarnings(_ => _.Ignore(CoreEventId.RowLimitingOperationWithoutOrderByWarning)); 23 | 24 | protected override void OnModelCreating(ModelBuilder builder) 25 | { 26 | builder.Entity() 27 | .HasMany(_ => _.Employees) 28 | .WithOne(_ => _.Company) 29 | .IsRequired(); 30 | builder.Entity(); 31 | builder.Entity() 32 | .HasMany(x => x.Devices) 33 | .WithMany(x => x.Employees) 34 | .UsingEntity("EmployeeDevice"); 35 | var order = builder.Entity(); 36 | order.OwnsOne(_ => _.BillingAddress); 37 | order.OwnsOne(_ => _.ShippingAddress); 38 | } 39 | } -------------------------------------------------------------------------------- /src/SampleWeb/DataContext/StreetAddress.cs: -------------------------------------------------------------------------------- 1 | [Owned] 2 | public class StreetAddress 3 | { 4 | public string AddressLine1 { get; set; } = null!; 5 | public string? AddressLine2 { get; set; } 6 | public string State { get; set; } = null!; 7 | public string AreaCode { get; set; } = null!; 8 | public string Country { get; set; } = null!; 9 | } -------------------------------------------------------------------------------- /src/SampleWeb/FromGraphQL/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | static class StringExtensions 2 | { 3 | static JsonSerializerOptions jsonOptions = new() 4 | { 5 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase, 6 | Converters = 7 | { 8 | new ObjectDictionaryConverter() 9 | } 10 | }; 11 | 12 | /// 13 | /// Converts a JSON-formatted string into a dictionary. 14 | /// 15 | /// A JSON formatted string. 16 | /// Inputs. 17 | public static Inputs ToInputs(this string? json) 18 | { 19 | if (json == null) 20 | { 21 | return new(new Dictionary()); 22 | } 23 | 24 | var dictionary = json.ToDictionary(); 25 | return dictionary.ToInputs(); 26 | } 27 | 28 | /// 29 | /// Converts a JSON-formatted string into a dictionary of objects of their actual type. 30 | /// 31 | /// The json. 32 | /// Dictionary. 33 | public static Dictionary ToDictionary(this string json) 34 | { 35 | if (json == string.Empty) 36 | { 37 | return []; 38 | } 39 | 40 | return JsonSerializer.Deserialize>(json, jsonOptions)!; 41 | } 42 | } -------------------------------------------------------------------------------- /src/SampleWeb/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System.Buffers; 2 | global using System.ComponentModel.DataAnnotations.Schema; 3 | global using System.Text.Json; 4 | global using System.Text.Json.Serialization; 5 | global using GraphQL; 6 | global using GraphQL.EntityFramework; 7 | global using Microsoft.AspNetCore; 8 | global using Microsoft.EntityFrameworkCore; 9 | global using Microsoft.EntityFrameworkCore.Metadata; -------------------------------------------------------------------------------- /src/SampleWeb/GraphQlController.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.SystemTextJson; 2 | using GraphQL.Types; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | #region GraphQlController 6 | [Route("[controller]")] 7 | [ApiController] 8 | public class GraphQlController(ISchema schema, IDocumentExecuter executer) : 9 | Controller 10 | { 11 | static GraphQLSerializer writer = new(true); 12 | 13 | [HttpGet] 14 | public Task Get( 15 | [FromQuery] string query, 16 | [FromQuery] string? variables, 17 | [FromQuery] string? operationName, 18 | Cancel cancel) 19 | { 20 | var inputs = variables.ToInputs(); 21 | return Execute(query, operationName, inputs, cancel); 22 | } 23 | 24 | public class GraphQLQuery 25 | { 26 | public string? OperationName { get; set; } 27 | public string Query { get; set; } = null!; 28 | public string? Variables { get; set; } 29 | } 30 | 31 | [HttpPost] 32 | public Task Post( 33 | [FromBody]GraphQLQuery query, 34 | Cancel cancel) 35 | { 36 | var inputs = query.Variables.ToInputs(); 37 | return Execute(query.Query, query.OperationName, inputs, cancel); 38 | } 39 | 40 | async Task Execute(string query, 41 | string? operationName, 42 | Inputs? variables, 43 | Cancel cancel) 44 | { 45 | var options = new ExecutionOptions 46 | { 47 | Schema = schema, 48 | Query = query, 49 | OperationName = operationName, 50 | Variables = variables, 51 | CancellationToken = cancel, 52 | ThrowOnUnhandledException = true, 53 | EnableMetrics = true, 54 | }; 55 | var executeAsync = await executer.ExecuteAsync(options); 56 | 57 | await writer.WriteAsync(Response.Body, executeAsync, cancel); 58 | } 59 | } 60 | #endregion -------------------------------------------------------------------------------- /src/SampleWeb/Graphs/CompanyGraphType.cs: -------------------------------------------------------------------------------- 1 | public class CompanyGraphType : 2 | EfObjectGraphType 3 | { 4 | public CompanyGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddNavigationListField( 8 | name: "employees", 9 | resolve: _ => _.Source.Employees); 10 | AddNavigationConnectionField( 11 | name: "employeesConnection", 12 | resolve: _ => _.Source.Employees, 13 | includeNames: ["Employees"]); 14 | AutoMap(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/SampleWeb/Graphs/DeviceGraphType.cs: -------------------------------------------------------------------------------- 1 | public class DeviceGraphType : 2 | EfObjectGraphType 3 | { 4 | public DeviceGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddNavigationListField( 8 | name: "employees", 9 | resolve: _ => _.Source.Employees); 10 | AutoMap(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/SampleWeb/Graphs/EmployeeGraphType.cs: -------------------------------------------------------------------------------- 1 | public class EmployeeGraphType : 2 | EfObjectGraphType 3 | { 4 | public EmployeeGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/SampleWeb/Graphs/EmployeeSummary.cs: -------------------------------------------------------------------------------- 1 | public class EmployeeSummary 2 | { 3 | public int CompanyId { get; set; } 4 | public double AverageAge { get; set; } 5 | } -------------------------------------------------------------------------------- /src/SampleWeb/Graphs/EmployeeSummaryGraphType.cs: -------------------------------------------------------------------------------- 1 | public class EmployeeSummaryGraphType : 2 | EfObjectGraphType 3 | { 4 | public EmployeeSummaryGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/SampleWeb/Graphs/OrderDetailGraphType.cs: -------------------------------------------------------------------------------- 1 | public class OrderDetailGraphType : 2 | EfObjectGraphType 3 | { 4 | public OrderDetailGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/SampleWeb/Program.cs: -------------------------------------------------------------------------------- 1 | public class Program 2 | { 3 | public static Task Main() 4 | { 5 | var webHostBuilder = WebHost.CreateDefaultBuilder(); 6 | var hostBuilder = webHostBuilder.UseStartup(); 7 | return hostBuilder.Build().RunAsync(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/SampleWeb/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:5000", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "SampleWeb": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "launchUrl": "graphiql", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | }, 18 | "applicationUrl": "http://localhost:5000" 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/SampleWeb/SampleWeb.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/SampleWeb/Schema.cs: -------------------------------------------------------------------------------- 1 | public class Schema : 2 | GraphQL.Types.Schema 3 | { 4 | public Schema(IServiceProvider provider, Query query) : 5 | base(provider) 6 | { 7 | RegisterTypeMapping(typeof(Employee), typeof(EmployeeGraphType)); 8 | RegisterTypeMapping(typeof(EmployeeSummary), typeof(EmployeeSummaryGraphType)); 9 | RegisterTypeMapping(typeof(Company), typeof(CompanyGraphType)); 10 | RegisterTypeMapping(typeof(Device), typeof(DeviceGraphType)); 11 | Query = query; 12 | // Subscription = (Subscription)provider.GetService(typeof(Subscription)); 13 | } 14 | } -------------------------------------------------------------------------------- /src/SampleWeb/Startup.cs: -------------------------------------------------------------------------------- 1 | using GraphiQl; 2 | using GraphQL.Types; 3 | 4 | public class Startup 5 | { 6 | public void ConfigureServices(IServiceCollection services) 7 | { 8 | EfGraphQLConventions.RegisterInContainer( 9 | services, 10 | model: SampleDbContext.StaticModel); 11 | 12 | foreach (var type in GetGraphQlTypes()) 13 | { 14 | services.AddSingleton(type); 15 | } 16 | 17 | var dbContextBuilder = new DbContextBuilder(); 18 | services.AddSingleton(dbContextBuilder); 19 | services.AddSingleton>(_ => dbContextBuilder.BuildDbContext); 20 | services.AddScoped(_ => dbContextBuilder.BuildDbContext()); 21 | services.AddSingleton(); 22 | services.AddSingleton(); 23 | services.AddMvc(option => option.EnableEndpointRouting = false); 24 | services.AddGraphQL(null); 25 | } 26 | 27 | static IEnumerable GetGraphQlTypes() => 28 | typeof(Startup).Assembly 29 | .GetTypes() 30 | .Where(_ => !_.IsAbstract && 31 | (_.IsAssignableTo(typeof(IObjectGraphType)) || 32 | _.IsAssignableTo(typeof(IInputObjectGraphType)))); 33 | 34 | public void Configure(IApplicationBuilder builder) 35 | { 36 | builder.UseWebSockets(); 37 | //builder.UseGraphQLWebSockets(); 38 | builder.UseGraphiQl("/graphiql", "/graphql"); 39 | builder.UseMvc(); 40 | } 41 | } -------------------------------------------------------------------------------- /src/Snippets/Configuration.cs: -------------------------------------------------------------------------------- 1 | class Configuration 2 | { 3 | #region ModelBuilder 4 | static class ModelBuilder 5 | { 6 | public static IModel GetInstance() 7 | { 8 | var builder = new DbContextOptionsBuilder(); 9 | builder.UseSqlServer("Fake"); 10 | using var context = new MyDbContext(builder.Options); 11 | return context.Model; 12 | } 13 | } 14 | #endregion 15 | 16 | static void RegisterInContainerExplicit(IServiceCollection serviceCollection) => 17 | #region RegisterInContainer 18 | EfGraphQLConventions.RegisterInContainer( 19 | serviceCollection, 20 | model: ModelBuilder.GetInstance()); 21 | #endregion 22 | 23 | 24 | public class MyDbContext(DbContextOptions options) : 25 | DbContext(options); 26 | } -------------------------------------------------------------------------------- /src/Snippets/ConnectionRootQuery.cs: -------------------------------------------------------------------------------- 1 | class ConnectionRootQuery 2 | { 3 | #region ConnectionRootQuery 4 | 5 | public class Query : 6 | QueryGraphType 7 | { 8 | public Query(IEfGraphQLService graphQlService) 9 | : 10 | base(graphQlService) => 11 | AddQueryConnectionField( 12 | name: "companies", 13 | resolve: _ => _.DbContext.Companies.OrderBy(_ => _.Name)); 14 | } 15 | 16 | #endregion 17 | 18 | public class Company 19 | { 20 | public string Name { get; set; } = null!; 21 | } 22 | 23 | class CompanyGraph(IEfGraphQLService efGraphQlService) : 24 | EfObjectGraphType(efGraphQlService); 25 | 26 | public class MyDbContext : 27 | DbContext 28 | { 29 | public DbSet Companies { get; set; } = null!; 30 | } 31 | } -------------------------------------------------------------------------------- /src/Snippets/ConnectionTypedGraph.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | 3 | class ConnectionTypedGraph 4 | { 5 | #region ConnectionTypedGraph 6 | 7 | public class CompanyGraph : 8 | EfObjectGraphType 9 | { 10 | public CompanyGraph(IEfGraphQLService graphQlService) : 11 | base(graphQlService) => 12 | AddNavigationConnectionField( 13 | name: "employees", 14 | resolve: _ => _.Source.Employees); 15 | } 16 | 17 | #endregion 18 | 19 | internal class MyDbContext : 20 | DbContext; 21 | 22 | public class Company 23 | { 24 | public List Employees { get; set; } = null!; 25 | } 26 | 27 | public class Employee; 28 | 29 | public class EmployeeGraph : 30 | ObjectGraphType; 31 | } -------------------------------------------------------------------------------- /src/Snippets/EnumerableConnection.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | 3 | public class EnumerableConnection 4 | { 5 | public class CompanyGraph : 6 | ObjectGraphType 7 | { 8 | public CompanyGraph() 9 | { 10 | var builder = Connection("employees"); 11 | builder.Resolve(context => 12 | { 13 | var skip = context.First.GetValueOrDefault(0); 14 | var take = context.PageSize.GetValueOrDefault(10); 15 | var list = context.Source.Employees; 16 | var page = list 17 | .Skip(skip) 18 | .Take(take); 19 | return new Connection 20 | { 21 | TotalCount = list.Count, 22 | PageInfo = new() 23 | { 24 | HasNextPage = true, 25 | HasPreviousPage = false, 26 | StartCursor = skip.ToString(), 27 | EndCursor = Math 28 | .Min(list.Count, skip + take) 29 | .ToString() 30 | }, 31 | Edges = page 32 | .Select((item, index) => new Edge 33 | { 34 | Cursor = (index + skip).ToString(), 35 | Node = item 36 | }) 37 | .ToList() 38 | }; 39 | }); 40 | } 41 | } 42 | 43 | public class Company 44 | { 45 | public List Employees { get; set; } = null!; 46 | } 47 | 48 | public class Employee; 49 | 50 | public class EmployeeGraph : 51 | ObjectGraphType; 52 | } -------------------------------------------------------------------------------- /src/Snippets/GlobalFilterSnippets.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable UnusedParameter.Local 2 | 3 | public class GlobalFilterSnippets 4 | { 5 | #region add-filter 6 | 7 | public class MyEntity 8 | { 9 | public string? Property { get; set; } 10 | } 11 | 12 | #endregion 13 | 14 | public static void Add(ServiceCollection services) 15 | { 16 | #region add-filter 17 | 18 | var filters = new Filters(); 19 | filters.Add( 20 | (userContext, dbContext, userPrincipal, item) => item.Property != "Ignore"); 21 | EfGraphQLConventions.RegisterInContainer( 22 | services, 23 | resolveFilters: _ => filters); 24 | 25 | #endregion 26 | } 27 | 28 | public class MyDbContext : 29 | DbContext; 30 | } -------------------------------------------------------------------------------- /src/Snippets/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using GraphQL.EntityFramework; 2 | global using GraphQL.Types.Relay.DataObjects; 3 | global using Microsoft.EntityFrameworkCore; 4 | global using Microsoft.EntityFrameworkCore.Metadata; 5 | global using Microsoft.Extensions.DependencyInjection; -------------------------------------------------------------------------------- /src/Snippets/ResolveDbContextQuery.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | 3 | class ResolveDbContextQuery 4 | { 5 | #region QueryResolveDbContext 6 | 7 | public class Query : 8 | QueryGraphType 9 | { 10 | public Query(IEfGraphQLService graphQlService) : 11 | base(graphQlService) => 12 | Field>("oldCompanies") 13 | .Resolve(context => 14 | { 15 | // uses the base QueryGraphType to resolve the db context 16 | var dbContext = ResolveDbContext(context); 17 | return dbContext.Companies.Where(_ => _.Age > 10); 18 | }); 19 | } 20 | 21 | #endregion 22 | 23 | public class MyDbContext : 24 | DbContext 25 | { 26 | public IQueryable Companies { get; set; } = null!; 27 | } 28 | 29 | public class Company 30 | { 31 | public int Age { get; set; } 32 | } 33 | 34 | class CompanyGraph: 35 | EfObjectGraphType 36 | { 37 | public CompanyGraph(IEfGraphQLService efGraphQlService) : 38 | base(efGraphQlService) 39 | { 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Snippets/RootQuery.cs: -------------------------------------------------------------------------------- 1 | class RootQuery 2 | { 3 | #region rootQuery 4 | 5 | public class Query : 6 | QueryGraphType 7 | { 8 | public Query(IEfGraphQLService graphQlService) : 9 | base(graphQlService) 10 | { 11 | AddSingleField( 12 | resolve: _ => _.DbContext.Companies, 13 | name: "company"); 14 | AddQueryField( 15 | name: "companies", 16 | resolve: _ => _.DbContext.Companies); 17 | } 18 | } 19 | 20 | #endregion 21 | 22 | public class MyDbContext : 23 | DbContext 24 | { 25 | public IQueryable Companies { get; set; } = null!; 26 | } 27 | 28 | public class Company; 29 | 30 | class CompanyGraph(IEfGraphQLService efGraphQlService) : 31 | EfObjectGraphType(efGraphQlService); 32 | } -------------------------------------------------------------------------------- /src/Snippets/Snippets.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | Exe 5 | $(NoWarn);xUnit1051 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Snippets/TypedGraph.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | 3 | public class TypedGraph 4 | { 5 | #region typedGraph 6 | 7 | public class CompanyGraph : 8 | EfObjectGraphType 9 | { 10 | public CompanyGraph(IEfGraphQLService graphQlService) : 11 | base(graphQlService) 12 | { 13 | AddNavigationListField( 14 | name: "employees", 15 | resolve: _ => _.Source.Employees); 16 | AddNavigationConnectionField( 17 | name: "employeesConnection", 18 | resolve: _ => _.Source.Employees, 19 | includeNames: ["Employees"]); 20 | AutoMap(); 21 | } 22 | } 23 | 24 | #endregion 25 | 26 | public class Company 27 | { 28 | public object? Id { get; set; } 29 | public object? Content { get; set; } 30 | public List Employees { get; set; } = null!; 31 | } 32 | 33 | public class Employee; 34 | 35 | public class MyDbContext : 36 | DbContext 37 | { 38 | public IQueryable Companies { get; set; } = null!; 39 | } 40 | 41 | public class EmployeeGraph : 42 | ObjectGraphType; 43 | } -------------------------------------------------------------------------------- /src/Tests/CompressTests.Simple.verified.txt: -------------------------------------------------------------------------------- 1 | query($id:String!){companies(ids:[$id]){id}} -------------------------------------------------------------------------------- /src/Tests/CompressTests.cs: -------------------------------------------------------------------------------- 1 | public class CompressTests 2 | { 3 | [Fact] 4 | public Task Simple() 5 | { 6 | var query = 7 | """ 8 | query ($id: String!) 9 | { 10 | companies(ids:[$id]) 11 | { 12 | id 13 | } 14 | } 15 | """; 16 | return Verify(Compress.Query(query)); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=10_after=1_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: false, 5 | HasPreviousPage: false, 6 | StartCursor: 1, 7 | EndCursor: 9 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 1, 12 | Node: b 13 | }, 14 | { 15 | Cursor: 2, 16 | Node: c 17 | }, 18 | { 19 | Cursor: 3, 20 | Node: d 21 | }, 22 | { 23 | Cursor: 4, 24 | Node: e 25 | }, 26 | { 27 | Cursor: 5, 28 | Node: f 29 | }, 30 | { 31 | Cursor: 6, 32 | Node: g 33 | }, 34 | { 35 | Cursor: 7, 36 | Node: h 37 | }, 38 | { 39 | Cursor: 8, 40 | Node: i 41 | }, 42 | { 43 | Cursor: 9, 44 | Node: j 45 | } 46 | ], 47 | Items: [ 48 | b, 49 | c, 50 | d, 51 | e, 52 | f, 53 | g, 54 | h, 55 | i, 56 | j 57 | ] 58 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=10_after=null_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: false, 5 | HasPreviousPage: false, 6 | StartCursor: 0, 7 | EndCursor: 9 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 0, 12 | Node: a 13 | }, 14 | { 15 | Cursor: 1, 16 | Node: b 17 | }, 18 | { 19 | Cursor: 2, 20 | Node: c 21 | }, 22 | { 23 | Cursor: 3, 24 | Node: d 25 | }, 26 | { 27 | Cursor: 4, 28 | Node: e 29 | }, 30 | { 31 | Cursor: 5, 32 | Node: f 33 | }, 34 | { 35 | Cursor: 6, 36 | Node: g 37 | }, 38 | { 39 | Cursor: 7, 40 | Node: h 41 | }, 42 | { 43 | Cursor: 8, 44 | Node: i 45 | }, 46 | { 47 | Cursor: 9, 48 | Node: j 49 | } 50 | ], 51 | Items: [ 52 | a, 53 | b, 54 | c, 55 | d, 56 | e, 57 | f, 58 | g, 59 | h, 60 | i, 61 | j 62 | ] 63 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=11_after=1_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: false, 5 | HasPreviousPage: false, 6 | StartCursor: 1, 7 | EndCursor: 9 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 1, 12 | Node: b 13 | }, 14 | { 15 | Cursor: 2, 16 | Node: c 17 | }, 18 | { 19 | Cursor: 3, 20 | Node: d 21 | }, 22 | { 23 | Cursor: 4, 24 | Node: e 25 | }, 26 | { 27 | Cursor: 5, 28 | Node: f 29 | }, 30 | { 31 | Cursor: 6, 32 | Node: g 33 | }, 34 | { 35 | Cursor: 7, 36 | Node: h 37 | }, 38 | { 39 | Cursor: 8, 40 | Node: i 41 | }, 42 | { 43 | Cursor: 9, 44 | Node: j 45 | } 46 | ], 47 | Items: [ 48 | b, 49 | c, 50 | d, 51 | e, 52 | f, 53 | g, 54 | h, 55 | i, 56 | j 57 | ] 58 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=11_after=null_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: false, 5 | HasPreviousPage: false, 6 | StartCursor: 0, 7 | EndCursor: 9 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 0, 12 | Node: a 13 | }, 14 | { 15 | Cursor: 1, 16 | Node: b 17 | }, 18 | { 19 | Cursor: 2, 20 | Node: c 21 | }, 22 | { 23 | Cursor: 3, 24 | Node: d 25 | }, 26 | { 27 | Cursor: 4, 28 | Node: e 29 | }, 30 | { 31 | Cursor: 5, 32 | Node: f 33 | }, 34 | { 35 | Cursor: 6, 36 | Node: g 37 | }, 38 | { 39 | Cursor: 7, 40 | Node: h 41 | }, 42 | { 43 | Cursor: 8, 44 | Node: i 45 | }, 46 | { 47 | Cursor: 9, 48 | Node: j 49 | } 50 | ], 51 | Items: [ 52 | a, 53 | b, 54 | c, 55 | d, 56 | e, 57 | f, 58 | g, 59 | h, 60 | i, 61 | j 62 | ] 63 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=1_after=0_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: true, 5 | HasPreviousPage: false, 6 | StartCursor: 0, 7 | EndCursor: 0 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 0, 12 | Node: a 13 | } 14 | ], 15 | Items: [ 16 | a 17 | ] 18 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=2_after=1_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: true, 5 | HasPreviousPage: true, 6 | StartCursor: 1, 7 | EndCursor: 2 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 1, 12 | Node: b 13 | }, 14 | { 15 | Cursor: 2, 16 | Node: c 17 | } 18 | ], 19 | Items: [ 20 | b, 21 | c 22 | ] 23 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=2_after=null_last=null_before=2.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: true, 5 | HasPreviousPage: false, 6 | StartCursor: 0, 7 | EndCursor: 1 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 0, 12 | Node: a 13 | }, 14 | { 15 | Cursor: 1, 16 | Node: b 17 | } 18 | ], 19 | Items: [ 20 | a, 21 | b 22 | ] 23 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=2_after=null_last=null_before=3.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: true, 5 | HasPreviousPage: true, 6 | StartCursor: 1, 7 | EndCursor: 2 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 1, 12 | Node: b 13 | }, 14 | { 15 | Cursor: 2, 16 | Node: c 17 | } 18 | ], 19 | Items: [ 20 | b, 21 | c 22 | ] 23 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=2_after=null_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: true, 5 | HasPreviousPage: false, 6 | StartCursor: 0, 7 | EndCursor: 1 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 0, 12 | Node: a 13 | }, 14 | { 15 | Cursor: 1, 16 | Node: b 17 | } 18 | ], 19 | Items: [ 20 | a, 21 | b 22 | ] 23 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=3_after=1_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: true, 5 | HasPreviousPage: true, 6 | StartCursor: 1, 7 | EndCursor: 3 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 1, 12 | Node: b 13 | }, 14 | { 15 | Cursor: 2, 16 | Node: c 17 | }, 18 | { 19 | Cursor: 3, 20 | Node: d 21 | } 22 | ], 23 | Items: [ 24 | b, 25 | c, 26 | d 27 | ] 28 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=3_after=null_last=null_before=2.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: true, 5 | HasPreviousPage: false, 6 | StartCursor: 0, 7 | EndCursor: 2 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 0, 12 | Node: a 13 | }, 14 | { 15 | Cursor: 1, 16 | Node: b 17 | }, 18 | { 19 | Cursor: 2, 20 | Node: c 21 | } 22 | ], 23 | Items: [ 24 | a, 25 | b, 26 | c 27 | ] 28 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=3_after=null_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: true, 5 | HasPreviousPage: false, 6 | StartCursor: 0, 7 | EndCursor: 2 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 0, 12 | Node: a 13 | }, 14 | { 15 | Cursor: 1, 16 | Node: b 17 | }, 18 | { 19 | Cursor: 2, 20 | Node: c 21 | } 22 | ], 23 | Items: [ 24 | a, 25 | b, 26 | c 27 | ] 28 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=null_after=7_last=2_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: false, 5 | HasPreviousPage: true, 6 | StartCursor: 8, 7 | EndCursor: 9 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 8, 12 | Node: j 13 | }, 14 | { 15 | Cursor: 9, 16 | Node: i 17 | } 18 | ], 19 | Items: [ 20 | j, 21 | i 22 | ] 23 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=null_after=null_last=2_before=8.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: true, 5 | HasPreviousPage: true, 6 | StartCursor: 6, 7 | EndCursor: 7 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 6, 12 | Node: h 13 | }, 14 | { 15 | Cursor: 7, 16 | Node: g 17 | } 18 | ], 19 | Items: [ 20 | h, 21 | g 22 | ] 23 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.List_first=null_after=null_last=2_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | TotalCount: 10, 3 | PageInfo: { 4 | HasNextPage: false, 5 | HasPreviousPage: true, 6 | StartCursor: 8, 7 | EndCursor: 9 8 | }, 9 | Edges: [ 10 | { 11 | Cursor: 8, 12 | Node: j 13 | }, 14 | { 15 | Cursor: 9, 16 | Node: i 17 | } 18 | ], 19 | Items: [ 20 | j, 21 | i 22 | ] 23 | } -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=10_after=1_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: c 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: d 9 | }, 10 | { 11 | Id: Guid_3, 12 | Property: e 13 | }, 14 | { 15 | Id: Guid_4, 16 | Property: f 17 | }, 18 | { 19 | Id: Guid_5, 20 | Property: g 21 | }, 22 | { 23 | Id: Guid_6, 24 | Property: h 25 | }, 26 | { 27 | Id: Guid_7, 28 | Property: i 29 | }, 30 | { 31 | Id: Guid_8, 32 | Property: j 33 | } 34 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=10_after=null_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: a 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: b 9 | }, 10 | { 11 | Id: Guid_3, 12 | Property: c 13 | }, 14 | { 15 | Id: Guid_4, 16 | Property: d 17 | }, 18 | { 19 | Id: Guid_5, 20 | Property: e 21 | }, 22 | { 23 | Id: Guid_6, 24 | Property: f 25 | }, 26 | { 27 | Id: Guid_7, 28 | Property: g 29 | }, 30 | { 31 | Id: Guid_8, 32 | Property: h 33 | }, 34 | { 35 | Id: Guid_9, 36 | Property: i 37 | }, 38 | { 39 | Id: Guid_10, 40 | Property: j 41 | } 42 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=11_after=1_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: c 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: d 9 | }, 10 | { 11 | Id: Guid_3, 12 | Property: e 13 | }, 14 | { 15 | Id: Guid_4, 16 | Property: f 17 | }, 18 | { 19 | Id: Guid_5, 20 | Property: g 21 | }, 22 | { 23 | Id: Guid_6, 24 | Property: h 25 | }, 26 | { 27 | Id: Guid_7, 28 | Property: i 29 | }, 30 | { 31 | Id: Guid_8, 32 | Property: j 33 | } 34 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=11_after=null_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: a 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: b 9 | }, 10 | { 11 | Id: Guid_3, 12 | Property: c 13 | }, 14 | { 15 | Id: Guid_4, 16 | Property: d 17 | }, 18 | { 19 | Id: Guid_5, 20 | Property: e 21 | }, 22 | { 23 | Id: Guid_6, 24 | Property: f 25 | }, 26 | { 27 | Id: Guid_7, 28 | Property: g 29 | }, 30 | { 31 | Id: Guid_8, 32 | Property: h 33 | }, 34 | { 35 | Id: Guid_9, 36 | Property: i 37 | }, 38 | { 39 | Id: Guid_10, 40 | Property: j 41 | } 42 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=1_after=0_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: b 5 | } 6 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=2_after=1_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: c 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: d 9 | } 10 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=2_after=null_last=null_before=2.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: a 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: b 9 | } 10 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=2_after=null_last=null_before=3.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: b 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: c 9 | } 10 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=2_after=null_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: a 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: b 9 | } 10 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=3_after=1_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: c 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: d 9 | }, 10 | { 11 | Id: Guid_3, 12 | Property: e 13 | } 14 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=3_after=null_last=null_before=2.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: a 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: b 9 | }, 10 | { 11 | Id: Guid_3, 12 | Property: c 13 | } 14 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=3_after=null_last=null_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: a 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: b 9 | }, 10 | { 11 | Id: Guid_3, 12 | Property: c 13 | } 14 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=null_after=7_last=2_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: i 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: j 9 | } 10 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=null_after=null_last=2_before=8.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: g 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: h 9 | } 10 | ] -------------------------------------------------------------------------------- /src/Tests/ConnectionConverter/ConnectionConverterTests.Queryable_first=null_after=null_last=2_before=null.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | Property: i 5 | }, 6 | { 7 | Id: Guid_2, 8 | Property: j 9 | } 10 | ] -------------------------------------------------------------------------------- /src/Tests/DependencyResolutionTests/DependencyDbContext.cs: -------------------------------------------------------------------------------- 1 | public class DependencyDbContext(DbContextOptions options) : 2 | DbContext(options) 3 | { 4 | public DbSet Entities { get; set; } = null!; 5 | 6 | protected override void OnModelCreating(ModelBuilder modelBuilder) => modelBuilder.Entity(); 7 | } -------------------------------------------------------------------------------- /src/Tests/DependencyResolutionTests/DependencyQuery.cs: -------------------------------------------------------------------------------- 1 | public class DependencyQuery : 2 | QueryGraphType 3 | { 4 | public DependencyQuery(IEfGraphQLService efGraphQlService) : 5 | base(efGraphQlService) => 6 | AddQueryField( 7 | name: "entities", 8 | resolve: _ => _.DbContext.Entities); 9 | } -------------------------------------------------------------------------------- /src/Tests/DependencyResolutionTests/DependencySchema.cs: -------------------------------------------------------------------------------- 1 | public class DependencySchema : 2 | GraphQL.Types.Schema 3 | { 4 | public DependencySchema(IServiceProvider provider) : 5 | base(provider) 6 | { 7 | RegisterTypeMapping(typeof(Entity), typeof(EntityGraphType)); 8 | Query = provider.GetRequiredService(); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Tests/DependencyResolutionTests/DependencyTests.ExplicitModel.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "entities": [ 4 | { 5 | "property": "A" 6 | } 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /src/Tests/DependencyResolutionTests/DependencyTests.ScopedDbContext.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "entities": [ 4 | { 5 | "property": "A" 6 | } 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /src/Tests/DependencyResolutionTests/DependencyTests.SingletonDbContext.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "entities": [ 4 | { 5 | "property": "A" 6 | } 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /src/Tests/DependencyResolutionTests/DependencyTests.TransientDbContext.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "entities": [ 4 | { 5 | "property": "A" 6 | } 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /src/Tests/DependencyResolutionTests/Entity.cs: -------------------------------------------------------------------------------- 1 | public class Entity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | } -------------------------------------------------------------------------------- /src/Tests/DependencyResolutionTests/EntityGraphType.cs: -------------------------------------------------------------------------------- 1 | public class EntityGraphType : 2 | EfObjectGraphType 3 | { 4 | public EntityGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | Field(_ => _.Id); 8 | Field(_ => _.Property); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Tests/GlobalFiltersTests.cs: -------------------------------------------------------------------------------- 1 | public class GlobalFiltersTests 2 | { 3 | [Fact] 4 | public async Task Simple() 5 | { 6 | var filters = new Filters(); 7 | filters.Add((_, _, _, target) => target.Property != "Ignore"); 8 | Assert.True(await filters.ShouldInclude(new(), new(), null, new Target())); 9 | Assert.False(await filters.ShouldInclude(new(), new(), null, null)); 10 | Assert.True(await filters.ShouldInclude(new(), new(), null, new Target {Property = "Include"})); 11 | Assert.False(await filters.ShouldInclude(new(), new(), null, new Target {Property = "Ignore"})); 12 | 13 | filters.Add((_, _, _, target) => target.Property != "Ignore"); 14 | Assert.True(await filters.ShouldInclude(new(), new(), null, new ChildTarget())); 15 | Assert.True(await filters.ShouldInclude(new(), new(), null, new ChildTarget {Property = "Include"})); 16 | Assert.False(await filters.ShouldInclude(new(), new(), null, new ChildTarget {Property = "Ignore"})); 17 | 18 | filters.Add((_, _, _, target) => target.Property != "Ignore"); 19 | Assert.True(await filters.ShouldInclude(new(), new(), null, new ImplementationTarget())); 20 | Assert.True(await filters.ShouldInclude(new(), new(), null, new ImplementationTarget { Property = "Include"})); 21 | Assert.False(await filters.ShouldInclude(new(), new(), null, new ImplementationTarget { Property = "Ignore" })); 22 | 23 | Assert.True(await filters.ShouldInclude(new(), new(), null, new NonTarget { Property = "Foo" })); 24 | } 25 | 26 | public class MyContext : DbContext; 27 | 28 | public class NonTarget 29 | { 30 | public string? Property { get; set; } 31 | } 32 | public class Target 33 | { 34 | public string? Property { get; set; } 35 | } 36 | 37 | public class ChildTarget : 38 | BaseTarget; 39 | 40 | public class BaseTarget 41 | { 42 | public string? Property { get; set; } 43 | } 44 | 45 | public class ImplementationTarget : 46 | ITarget 47 | { 48 | public string? Property { get; set; } 49 | } 50 | 51 | public interface ITarget 52 | { 53 | string? Property { get; set; } 54 | } 55 | } -------------------------------------------------------------------------------- /src/Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System.ComponentModel.DataAnnotations.Schema; 2 | global using Polyfills; 3 | -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Child1Entity.cs: -------------------------------------------------------------------------------- 1 | public class Child1Entity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public Guid? ParentId { get; set; } 5 | public WithManyChildrenEntity? Parent { get; set; } 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Child1GraphType.cs: -------------------------------------------------------------------------------- 1 | public class Child1GraphType : 2 | EfObjectGraphType 3 | { 4 | public Child1GraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Child2Entity.cs: -------------------------------------------------------------------------------- 1 | public class Child2Entity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public Guid? ParentId { get; set; } 5 | public WithManyChildrenEntity? Parent { get; set; } 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Child2GraphType.cs: -------------------------------------------------------------------------------- 1 | public class Child2GraphType : 2 | EfObjectGraphType 3 | { 4 | public Child2GraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ChildEntity.cs: -------------------------------------------------------------------------------- 1 | public class ChildEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | public int? Nullable { get; set; } 6 | public Guid? ParentId { get; set; } 7 | public ParentEntity? Parent { get; set; } 8 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ChildGraphType.cs: -------------------------------------------------------------------------------- 1 | public class ChildGraphType : 2 | EfObjectGraphType 3 | { 4 | public ChildGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddNavigationField( 8 | name: "parentAlias", 9 | resolve: _ => _.Source.Parent, 10 | graphType: typeof(ParentGraphType), 11 | includeNames: ["Parent"]); 12 | AutoMap(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/CustomTypeEntity.cs: -------------------------------------------------------------------------------- 1 | public class CustomTypeEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public long Property { get; set; } 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/CustomTypeGraphType.cs: -------------------------------------------------------------------------------- 1 | public class CustomTypeGraphType : 2 | EfObjectGraphType 3 | { 4 | public CustomTypeGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/DateEntity.cs: -------------------------------------------------------------------------------- 1 | public class DateEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public Date? Property { get; set; } 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/DateEntityGraphType.cs: -------------------------------------------------------------------------------- 1 | public class DateEntityGraphType : 2 | EfObjectGraphType 3 | { 4 | public DateEntityGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/EnumEntity.cs: -------------------------------------------------------------------------------- 1 | public class EnumEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public DayOfWeek? Property { get; set; } 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/EnumEntityGraphType.cs: -------------------------------------------------------------------------------- 1 | public class EnumEntityGraphType : 2 | EfObjectGraphType 3 | { 4 | public EnumEntityGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Filtering/FilterChildEntity.cs: -------------------------------------------------------------------------------- 1 | public class FilterChildEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | public FilterParentEntity? Parent { get; set; } 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Filtering/FilterChildGraphType.cs: -------------------------------------------------------------------------------- 1 | public class FilterChildGraphType : 2 | EfObjectGraphType 3 | { 4 | public FilterChildGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Filtering/FilterParentEntity.cs: -------------------------------------------------------------------------------- 1 | public class FilterParentEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | public IList Children { get; set; } = []; 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Filtering/FilterParentGraphType.cs: -------------------------------------------------------------------------------- 1 | public class FilterParentGraphType : 2 | EfObjectGraphType 3 | { 4 | public FilterParentGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddNavigationConnectionField( 8 | name: "childrenConnection", 9 | resolve: _ => _.Source.Children, 10 | includeNames: ["Children"]); 11 | AutoMap(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/IncludeNonQueryable/IncludeNonQueryableA.cs: -------------------------------------------------------------------------------- 1 | public class IncludeNonQueryableA 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public Guid IncludeNonQueryableBId { get; set; } 5 | public IncludeNonQueryableB IncludeNonQueryableB { get; set; } = null!; 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/IncludeNonQueryable/IncludeNonQueryableAGraphType.cs: -------------------------------------------------------------------------------- 1 | public class IncludeNonQueryableAGraphType : 2 | EfObjectGraphType 3 | { 4 | public IncludeNonQueryableAGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/IncludeNonQueryable/IncludeNonQueryableB.cs: -------------------------------------------------------------------------------- 1 | public class IncludeNonQueryableB 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public Guid IncludeNonQueryableAId { get; set; } 5 | public IncludeNonQueryableA IncludeNonQueryableA { get; set; } = null!; 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/IncludeNonQueryable/IncludeNonQueryableBGraphType.cs: -------------------------------------------------------------------------------- 1 | public class IncludeNonQueryableBGraphType : 2 | EfObjectGraphType 3 | { 4 | public IncludeNonQueryableBGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Inheritance/DerivedChildEntity.cs: -------------------------------------------------------------------------------- 1 | public class DerivedChildEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | public Guid? ParentId { get; set; } 6 | public InheritedEntity? Parent { get; set; } 7 | public Guid? TypedParentId { get; set; } 8 | public DerivedWithNavigationEntity? TypedParent { get; set; } 9 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Inheritance/DerivedChildGraphType.cs: -------------------------------------------------------------------------------- 1 | public class DerivedChildGraphType : 2 | EfObjectGraphType 3 | { 4 | public DerivedChildGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(["Parent", "TypedParent"]); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Inheritance/DerivedEntity.cs: -------------------------------------------------------------------------------- 1 |  2 | public class DerivedEntity : 3 | InheritedEntity; -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Inheritance/DerivedGraphType.cs: -------------------------------------------------------------------------------- 1 | public class DerivedGraphType : 2 | EfObjectGraphType 3 | { 4 | public DerivedGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddNavigationConnectionField( 8 | name: "childrenFromInterface", 9 | _ => _.Source.ChildrenFromBase); 10 | AutoMap(); 11 | Interface(); 12 | IsTypeOf = obj => obj is DerivedEntity; 13 | } 14 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Inheritance/DerivedWithNavigationEntity.cs: -------------------------------------------------------------------------------- 1 | public class DerivedWithNavigationEntity : InheritedEntity 2 | { 3 | public IList Children { get; set; } = []; 4 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Inheritance/DerivedWithNavigationGraphType.cs: -------------------------------------------------------------------------------- 1 | public class DerivedWithNavigationGraphType : 2 | EfObjectGraphType 3 | { 4 | public DerivedWithNavigationGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddNavigationConnectionField( 8 | name: "childrenFromInterface", 9 | _ => _.Source.ChildrenFromBase); 10 | AddNavigationConnectionField( 11 | name: "childrenFromDerived", 12 | _ => _.Source.Children, 13 | includeNames: [ "Children" ]); 14 | AutoMap(); 15 | Interface(); 16 | IsTypeOf = obj => obj is DerivedWithNavigationEntity; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Inheritance/InheritedEntity.cs: -------------------------------------------------------------------------------- 1 | public abstract class InheritedEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | public IList ChildrenFromBase { get; set; } = []; 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Inheritance/InterfaceGraphType.cs: -------------------------------------------------------------------------------- 1 | public class InterfaceGraphType : 2 | EfInterfaceGraphType 3 | { 4 | public InterfaceGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | Field(_ => _.Id); 8 | Field(_ => _.Property, nullable: true); 9 | AddNavigationConnectionField( 10 | name: "childrenFromInterface", 11 | includeNames: [ "ChildrenFromBase" ]); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Levels/Level1Entity.cs: -------------------------------------------------------------------------------- 1 | public class Level1Entity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public int? Level2EntityId { get; set; } 5 | public Level2Entity? Level2Entity { get; set; } 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Levels/Level1GraphType.cs: -------------------------------------------------------------------------------- 1 | public class Level1GraphType : 2 | EfObjectGraphType 3 | { 4 | public Level1GraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Levels/Level2Entity.cs: -------------------------------------------------------------------------------- 1 | public class Level2Entity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public Guid? Level3EntityId { get; set; } 5 | public Level3Entity? Level3Entity { get; set; } 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Levels/Level2GraphType.cs: -------------------------------------------------------------------------------- 1 | public class Level2GraphType : 2 | EfObjectGraphType 3 | { 4 | public Level2GraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Levels/Level3Entity.cs: -------------------------------------------------------------------------------- 1 | public class Level3Entity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Levels/Level3GraphType.cs: -------------------------------------------------------------------------------- 1 | public class Level3GraphType : 2 | EfObjectGraphType 3 | { 4 | public Level3GraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ManyToMany/ManyToManyLeftEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | public class ManyToManyLeftEntity 4 | { 5 | [Key] 6 | public string Id { get; set; } = null!; 7 | 8 | public string? LeftName { get; set; } 9 | 10 | public IList Rights { get; set; } = []; 11 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ManyToMany/ManyToManyLeftGraphType.cs: -------------------------------------------------------------------------------- 1 | public class ManyToManyLeftGraphType : 2 | EfObjectGraphType 3 | { 4 | public ManyToManyLeftGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ManyToMany/ManyToManyMiddleEntity.cs: -------------------------------------------------------------------------------- 1 | public class ManyToManyMiddleEntity 2 | { 3 | public ManyToManyLeftEntity ManyToManyLeftEntity { get; set; } = null!; 4 | public ManyToManyRightEntity ManyToManyRightEntity { get; set; } = null!; 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ManyToMany/ManyToManyRightEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | public class ManyToManyRightEntity 4 | { 5 | [Key] 6 | public string Id { get; set; } = null!; 7 | 8 | public string? RightName { get; set; } 9 | 10 | public IList Lefts { get; set; } = []; 11 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ManyToMany/ManyToManyRightGraphType.cs: -------------------------------------------------------------------------------- 1 | public class ManyToManyRightGraphType : 2 | EfObjectGraphType 3 | { 4 | public ManyToManyRightGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ManyToMany/ManyToManyShadowLeftEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | public class ManyToManyShadowLeftEntity 4 | { 5 | [Key] 6 | public string Id { get; set; } = null!; 7 | 8 | [ForeignKey("ManyToManyShadowLeftEntityId")] 9 | [InverseProperty("ManyToManyShadowLeftEntities")] 10 | public virtual ICollection ManyToManyShadowRightEntities { get; set; } = []; 11 | } 12 | -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ManyToMany/ManyToManyShadowRightEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | public class ManyToManyShadowRightEntity 4 | { 5 | [Key] 6 | public string Id { get; set; } = null!; 7 | 8 | [ForeignKey("ManyToManyShadowRightEntityId")] 9 | [InverseProperty("ManyToManyShadowRightEntities")] 10 | public virtual ICollection ManyToManyShadowLeftEntities { get; set; } = []; 11 | } 12 | -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/MisNamed/WithMisNamedQueryChildEntity.cs: -------------------------------------------------------------------------------- 1 | public class WithMisNamedQueryChildEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public Guid? ParentId { get; set; } 5 | public WithMisNamedQueryParentEntity? Parent { get; set; } 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/MisNamed/WithMisNamedQueryChildGraphType.cs: -------------------------------------------------------------------------------- 1 | public class WithMisNamedQueryChildGraphType : 2 | EfObjectGraphType 3 | { 4 | public WithMisNamedQueryChildGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/MisNamed/WithMisNamedQueryParentEntity.cs: -------------------------------------------------------------------------------- 1 | public class WithMisNamedQueryParentEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public IList Children { get; set; } = []; 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/MisNamed/WithMisNamedQueryParentGraphType.cs: -------------------------------------------------------------------------------- 1 | public class WithMisNamedQueryParentGraphType : 2 | EfObjectGraphType 3 | { 4 | public WithMisNamedQueryParentGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddQueryField( 8 | name: "misNamedChildren", 9 | resolve: context => 10 | { 11 | var parentId = context.Source.Id; 12 | return context.DbContext.WithMisNamedQueryChildEntities 13 | .Where(_ => _.ParentId == parentId); 14 | }); 15 | AutoMap(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/NamedId/NamedIdEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | public class NamedIdEntity 4 | { 5 | [Key] 6 | public Guid NamedId { get; set; } = Guid.NewGuid(); 7 | public string? Property { get; set; } 8 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/NamedId/NamedIdGraphType.cs: -------------------------------------------------------------------------------- 1 | public class NamedIdGraphType : 2 | EfObjectGraphType 3 | { 4 | public NamedIdGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Nullable/WithNullableEntity.cs: -------------------------------------------------------------------------------- 1 | public class WithNullableEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public int? Nullable { get; set; } 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Nullable/WithNullableGraphType.cs: -------------------------------------------------------------------------------- 1 | public class WithNullableGraphType : 2 | EfObjectGraphType 3 | { 4 | public WithNullableGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Owned/OwnedChild.cs: -------------------------------------------------------------------------------- 1 | [Owned] 2 | public class OwnedChild 3 | { 4 | public string Property { get; set; } = null!; 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Owned/OwnedChildGraphType.cs: -------------------------------------------------------------------------------- 1 | public class OwnedChildGraphType : 2 | EfObjectGraphType 3 | { 4 | public OwnedChildGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Owned/OwnedParent.cs: -------------------------------------------------------------------------------- 1 | public class OwnedParent 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | 6 | public OwnedChild Child1 { get; set; } = null!; 7 | public OwnedChild Child2 { get; set; } = null!; 8 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/Owned/OwnedParentGraphType.cs: -------------------------------------------------------------------------------- 1 | public class OwnedParentGraphType : 2 | EfObjectGraphType 3 | { 4 | public OwnedParentGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ParentEntity.cs: -------------------------------------------------------------------------------- 1 | public class ParentEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | public IList Children { get; set; } = []; 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ParentEntityView.cs: -------------------------------------------------------------------------------- 1 | public class ParentEntityView 2 | { 3 | public string? Property { get; set; } 4 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ParentEntityViewGraphType.cs: -------------------------------------------------------------------------------- 1 | public class ParentEntityViewGraphType : 2 | EfObjectGraphType 3 | { 4 | public ParentEntityViewGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/ParentGraphType.cs: -------------------------------------------------------------------------------- 1 | public class ParentGraphType : 2 | EfObjectGraphType 3 | { 4 | public ParentGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddNavigationConnectionField( 8 | name: "childrenConnection", 9 | resolve: _ => _.Source.Children, 10 | includeNames: [ "Children" ]); 11 | AddNavigationConnectionField( 12 | name: "childrenConnectionOmitQueryArguments", 13 | resolve: _ => _.Source.Children, 14 | includeNames: [ "Children" ], 15 | omitQueryArguments: true); 16 | AutoMap(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/SkipLevelGraph.cs: -------------------------------------------------------------------------------- 1 | public class SkipLevelGraph : 2 | EfObjectGraphType 3 | { 4 | public SkipLevelGraph(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddNavigationField( 8 | name: "level3Entity", 9 | resolve: _ => _.Source.Level2Entity?.Level3Entity, 10 | graphType: typeof(Level3GraphType), 11 | includeNames: [ "Level2Entity.Level3Entity" ]); 12 | AutoMap(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/StringEntity.cs: -------------------------------------------------------------------------------- 1 | public class StringEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public string? Property { get; set; } 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/StringEntityGraphType.cs: -------------------------------------------------------------------------------- 1 | public class StringEntityGraphType : 2 | EfObjectGraphType 3 | { 4 | public StringEntityGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/TimeEntity.cs: -------------------------------------------------------------------------------- 1 | public class TimeEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public Time? Property { get; set; } 5 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/TimeEntityGraphType.cs: -------------------------------------------------------------------------------- 1 | public class TimeEntityGraphType : 2 | EfObjectGraphType 3 | { 4 | public TimeEntityGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/WithManyChildrenEntity.cs: -------------------------------------------------------------------------------- 1 | public class WithManyChildrenEntity 2 | { 3 | public Guid Id { get; set; } = Guid.NewGuid(); 4 | public Child1Entity Child1 { get; set; } = null!; 5 | public Child2Entity Child2 { get; set; } = null!; 6 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Graphs/WithManyChildrenGraphType.cs: -------------------------------------------------------------------------------- 1 | public class WithManyChildrenGraphType : 2 | EfObjectGraphType 3 | { 4 | public WithManyChildrenGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) 6 | { 7 | AddNavigationField( 8 | name: "child1", 9 | resolve: context => 10 | { 11 | Assert.NotNull(context.Source.Child2); 12 | Assert.NotNull(context.Source.Child1); 13 | return context.Source.Child1; 14 | }, 15 | includeNames: [ "Child2", "Child1" ]); 16 | AutoMap(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Child_filtered.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntitiesFiltered": [ 6 | { 7 | "property": "Value1", 8 | "children": [ 9 | { 10 | "property": "Value3" 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | }, 17 | sql: { 18 | Text: 19 | select f.Id, 20 | f.Property, 21 | f0.Id, 22 | f0.ParentId, 23 | f0.Property 24 | from FilterParentEntities as f 25 | left outer join 26 | FilterChildEntities as f0 27 | on f.Id = f0.ParentId 28 | order by f.Id 29 | } 30 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Child_parent.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "childEntities": [ 6 | { 7 | "property": "Value2", 8 | "parent": { 9 | "property": "Value1" 10 | } 11 | }, 12 | { 13 | "property": "Value3", 14 | "parent": { 15 | "property": "Value1" 16 | } 17 | }, 18 | { 19 | "property": "Value5", 20 | "parent": { 21 | "property": "Value4" 22 | } 23 | } 24 | ] 25 | } 26 | }, 27 | sql: { 28 | Text: 29 | select c.Id, 30 | c.Nullable, 31 | c.ParentId, 32 | c.Property, 33 | p.Id, 34 | p.Property 35 | from ChildEntities as c 36 | left outer join 37 | ParentEntities as p 38 | on c.ParentId = p.Id 39 | order by c.Property 40 | } 41 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Child_parent_with_alias.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "childEntities": [ 6 | { 7 | "parentAlias": { 8 | "property": "Value1" 9 | } 10 | }, 11 | { 12 | "parentAlias": { 13 | "property": "Value1" 14 | } 15 | }, 16 | { 17 | "parentAlias": { 18 | "property": "Value4" 19 | } 20 | } 21 | ] 22 | } 23 | }, 24 | sql: { 25 | Text: 26 | select c.Id, 27 | c.Nullable, 28 | c.ParentId, 29 | c.Property, 30 | p.Id, 31 | p.Property 32 | from ChildEntities as c 33 | left outer join 34 | ParentEntities as p 35 | on c.ParentId = p.Id 36 | order by c.Property 37 | } 38 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Connection_first_page.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntitiesConnection": { 6 | "totalCount": 8, 7 | "edges": [ 8 | { 9 | "cursor": "1", 10 | "node": { 11 | "property": "Value2" 12 | } 13 | }, 14 | { 15 | "cursor": "2", 16 | "node": { 17 | "property": "Value3" 18 | } 19 | } 20 | ], 21 | "items": [ 22 | { 23 | "property": "Value2" 24 | }, 25 | { 26 | "property": "Value3" 27 | } 28 | ] 29 | } 30 | } 31 | }, 32 | sql: [ 33 | { 34 | Text: 35 | select COUNT(*) 36 | from ParentEntities as p 37 | }, 38 | { 39 | Text: 40 | select p.Id, 41 | p.Property 42 | from ParentEntities as p 43 | order by p.Id 44 | offset @__p_0 rows fetch next @__p_1 rows only, 45 | Parameters: { 46 | @__p_0: 1, 47 | @__p_1: 2 48 | } 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Connection_nested.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "id": "Guid_1", 8 | "childrenConnection": { 9 | "edges": [], 10 | "pageInfo": { 11 | "endCursor": "-1", 12 | "hasNextPage": false 13 | } 14 | } 15 | }, 16 | { 17 | "id": "Guid_2", 18 | "childrenConnection": { 19 | "edges": [], 20 | "pageInfo": { 21 | "endCursor": "-1", 22 | "hasNextPage": false 23 | } 24 | } 25 | }, 26 | { 27 | "id": "Guid_3", 28 | "childrenConnection": { 29 | "edges": [], 30 | "pageInfo": { 31 | "endCursor": "-1", 32 | "hasNextPage": false 33 | } 34 | } 35 | }, 36 | { 37 | "id": "Guid_4", 38 | "childrenConnection": { 39 | "edges": [], 40 | "pageInfo": { 41 | "endCursor": "-1", 42 | "hasNextPage": false 43 | } 44 | } 45 | }, 46 | { 47 | "id": "Guid_5", 48 | "childrenConnection": { 49 | "edges": [], 50 | "pageInfo": { 51 | "endCursor": "-1", 52 | "hasNextPage": false 53 | } 54 | } 55 | }, 56 | { 57 | "id": "Guid_6", 58 | "childrenConnection": { 59 | "edges": [], 60 | "pageInfo": { 61 | "endCursor": "-1", 62 | "hasNextPage": false 63 | } 64 | } 65 | }, 66 | { 67 | "id": "Guid_7", 68 | "childrenConnection": { 69 | "edges": [], 70 | "pageInfo": { 71 | "endCursor": "-1", 72 | "hasNextPage": false 73 | } 74 | } 75 | }, 76 | { 77 | "id": "Guid_8", 78 | "childrenConnection": { 79 | "edges": [], 80 | "pageInfo": { 81 | "endCursor": "-1", 82 | "hasNextPage": false 83 | } 84 | } 85 | } 86 | ] 87 | } 88 | }, 89 | sql: { 90 | Text: 91 | select p.Id, 92 | p.Property 93 | from ParentEntities as p 94 | } 95 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Connection_page_back.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntitiesConnection": { 6 | "totalCount": 8, 7 | "edges": [ 8 | { 9 | "cursor": "0", 10 | "node": { 11 | "property": "Value1" 12 | } 13 | }, 14 | { 15 | "cursor": "1", 16 | "node": { 17 | "property": "Value2" 18 | } 19 | } 20 | ], 21 | "items": [ 22 | { 23 | "property": "Value1" 24 | }, 25 | { 26 | "property": "Value2" 27 | } 28 | ] 29 | } 30 | } 31 | }, 32 | sql: [ 33 | { 34 | Text: 35 | select COUNT(*) 36 | from ParentEntities as p 37 | }, 38 | { 39 | Text: 40 | select p.Id, 41 | p.Property 42 | from ParentEntities as p 43 | order by p.Id 44 | offset @__p_0 rows fetch next @__p_1 rows only, 45 | Parameters: { 46 | @__p_0: 0, 47 | @__p_1: 2 48 | } 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Connection_parent_child_Filtered.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntitiesConnectionFiltered": { 6 | "totalCount": 1, 7 | "edges": [ 8 | { 9 | "cursor": "0", 10 | "node": { 11 | "property": "Value1", 12 | "children": [ 13 | { 14 | "property": "Value3" 15 | } 16 | ] 17 | } 18 | } 19 | ], 20 | "items": [ 21 | { 22 | "property": "Value1", 23 | "children": [ 24 | { 25 | "property": "Value3" 26 | } 27 | ] 28 | } 29 | ] 30 | } 31 | } 32 | }, 33 | sql: [ 34 | { 35 | Text: 36 | select COUNT(*) 37 | from FilterParentEntities as f 38 | }, 39 | { 40 | Text: 41 | select f1.Id, 42 | f1.Property, 43 | f0.Id, 44 | f0.ParentId, 45 | f0.Property 46 | from (select f.Id, 47 | f.Property 48 | from FilterParentEntities as f 49 | order by f.Id 50 | offset @__p_0 rows fetch next @__p_1 rows only) as f1 51 | left outer join 52 | FilterChildEntities as f0 53 | on f1.Id = f0.ParentId 54 | order by f1.Id, 55 | Parameters: { 56 | @__p_0: 0, 57 | @__p_1: 10 58 | } 59 | } 60 | ] 61 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.CustomType.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "customType": [ 6 | { 7 | "property": 3 8 | }, 9 | { 10 | "property": 9223372036854775807 11 | } 12 | ] 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select c.Id, 18 | c.Property 19 | from CustomTypeEntities as c 20 | order by c.Property 21 | } 22 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Explicit_Null.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: Exception, 3 | Message: Null 'id' is not supported. 4 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.FirstNullable_Found.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityNullableFirst": { 6 | "property": "Value1" 7 | } 8 | } 9 | }, 10 | sql: { 11 | Text: 12 | select top (1) p.Id, 13 | p.Property 14 | from ParentEntities as p 15 | where p.Id = 'Guid_1' 16 | } 17 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.FirstNullable_NotFound.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityNullableFirst": null 6 | } 7 | }, 8 | sql: { 9 | Text: 10 | select top (1) p.Id, 11 | p.Property 12 | from ParentEntities as p 13 | where p.Id = 'Guid_1' 14 | } 15 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.FirstParent_Child.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityFirst": { 6 | "property": "Value1", 7 | "children": [ 8 | { 9 | "property": "Value2" 10 | }, 11 | { 12 | "property": "Value3" 13 | } 14 | ] 15 | } 16 | } 17 | }, 18 | sql: { 19 | Text: 20 | select p0.Id, 21 | p0.Property, 22 | c.Id, 23 | c.Nullable, 24 | c.ParentId, 25 | c.Property 26 | from (select top (1) p.Id, 27 | p.Property 28 | from ParentEntities as p 29 | where p.Id = 'Guid_1') as p0 30 | left outer join 31 | ChildEntities as c 32 | on p0.Id = c.ParentId 33 | order by p0.Id 34 | } 35 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.FirstParent_Child_WithFragment.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityFirst": { 6 | "property": "Value1", 7 | "children": [ 8 | { 9 | "property": "Value2" 10 | }, 11 | { 12 | "property": "Value3" 13 | } 14 | ] 15 | } 16 | } 17 | }, 18 | sql: { 19 | Text: 20 | select p0.Id, 21 | p0.Property, 22 | c.Id, 23 | c.Nullable, 24 | c.ParentId, 25 | c.Property 26 | from (select top (1) p.Id, 27 | p.Property 28 | from ParentEntities as p 29 | where p.Id = 'Guid_1') as p0 30 | left outer join 31 | ChildEntities as c 32 | on p0.Id = c.ParentId 33 | order by p0.Id 34 | } 35 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.FirstParent_Child_mutation.verified.txt: -------------------------------------------------------------------------------- 1 | Cannot query field 'parentEntityMutationFirst' on type 'Mutation'. Did you mean 'parentEntityMutation'? -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.First_Found.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityFirst": { 6 | "property": "Value1" 7 | } 8 | } 9 | }, 10 | sql: { 11 | Text: 12 | select top (1) p.Id, 13 | p.Property 14 | from ParentEntities as p 15 | where p.Id = 'Guid_1' 16 | } 17 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.First_Found_Large_Text_NoAsync.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityFirst": { 6 | "id": "Guid_1" 7 | } 8 | } 9 | }, 10 | sql: { 11 | Text: 12 | select top (1) p.Id, 13 | p.Property 14 | from ParentEntities as p 15 | where p.Id = 'Guid_1' 16 | } 17 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.First_Found_NoTracking.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityFirst": { 6 | "property": "Value1" 7 | } 8 | } 9 | }, 10 | sql: { 11 | Text: 12 | select top (1) p.Id, 13 | p.Property 14 | from ParentEntities as p 15 | where p.Id = 'Guid_1' 16 | } 17 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.First_IdOnly.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityIdOnlyFirst": { 6 | "property": "Value1", 7 | "children": [ 8 | { 9 | "property": "Value2" 10 | } 11 | ] 12 | } 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select p0.Id, 18 | p0.Property, 19 | c.Id, 20 | c.Nullable, 21 | c.ParentId, 22 | c.Property 23 | from (select top (1) p.Id, 24 | p.Property 25 | from ParentEntities as p 26 | where p.Id = 'Guid_1') as p0 27 | left outer join 28 | ChildEntities as c 29 | on p0.Id = c.ParentId 30 | order by p0.Id 31 | } 32 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.First_NoArgs.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityWithNoArgsFirst": { 6 | "property": "Value1", 7 | "children": [ 8 | { 9 | "property": "Value2" 10 | } 11 | ] 12 | } 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select p0.Id, 18 | p0.Property, 19 | c.Id, 20 | c.Nullable, 21 | c.ParentId, 22 | c.Property 23 | from (select top (1) p.Id, 24 | p.Property 25 | from ParentEntities as p 26 | where p.Id = 'Guid_1') as p0 27 | left outer join 28 | ChildEntities as c 29 | on p0.Id = c.ParentId 30 | order by p0.Id 31 | } 32 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.First_NotFound.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | Type: FirstEntityNotFoundException, 4 | Message: Not found 5 | }, 6 | sql: { 7 | Text: 8 | select top (1) p.Id, 9 | p.Property 10 | from ParentEntities as p 11 | where p.Id = '00000000-0000-0000-0000-000000000001' 12 | } 13 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Id.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Id = 'Guid_1' 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Id_multiple.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value1" 8 | }, 9 | { 10 | "property": "Value2" 11 | } 12 | ] 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select p.Id, 18 | p.Property 19 | from ParentEntities as p 20 | where p.Id in ('Guid_1', 'Guid_2') 21 | } 22 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.In.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value2" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property = N'value2' 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.In_multiple.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value1" 8 | }, 9 | { 10 | "property": "Value2" 11 | } 12 | ] 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select p.Id, 18 | p.Property 19 | from ParentEntities as p 20 | where p.Property in (N'Value1', N'Value2') 21 | order by p.Property 22 | } 23 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Like.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value2" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property like N'value2' 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.LogQuery.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value3" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property is not null 18 | and p.Property like N'Valu%' 19 | and p.Property like N'%ue3' 20 | } 21 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.ManyToManyLeftWhereAndInclude.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "manyToManyRightEntities": [ 6 | { 7 | "rightName": "Right1", 8 | "lefts": [ 9 | { 10 | "leftName": "Left1" 11 | }, 12 | { 13 | "leftName": "Left2" 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | }, 20 | sql: { 21 | Text: 22 | select m.Id, 23 | m.RightName, 24 | s.ManyToManyLeftEntityId, 25 | s.ManyToManyRightEntityId, 26 | s.Id, 27 | s.LeftName 28 | from ManyToManyRightEntities as m 29 | left outer join 30 | (select m2.ManyToManyLeftEntityId, 31 | m2.ManyToManyRightEntityId, 32 | m3.Id, 33 | m3.LeftName 34 | from ManyToManyMiddleEntities as m2 35 | inner join 36 | ManyToManyLeftEntities as m3 37 | on m2.ManyToManyLeftEntityId = m3.Id) as s 38 | on m.Id = s.ManyToManyRightEntityId 39 | where exists (select 1 40 | from ManyToManyMiddleEntities as m0 41 | inner join 42 | ManyToManyLeftEntities as m1 43 | on m0.ManyToManyLeftEntityId = m1.Id 44 | where m.Id = m0.ManyToManyRightEntityId 45 | and m1.LeftName = N'Left2') 46 | order by m.Id, s.ManyToManyLeftEntityId, s.ManyToManyRightEntityId 47 | } 48 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.ManyToManyLeftWhereAndInclude_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "manyToManyRightEntities": [ 6 | { 7 | "rightName": "Right1", 8 | "lefts": [ 9 | { 10 | "leftName": "Left1" 11 | }, 12 | { 13 | "leftName": "Left2" 14 | } 15 | ] 16 | }, 17 | { 18 | "rightName": "Right2", 19 | "lefts": [ 20 | { 21 | "leftName": "Left1" 22 | } 23 | ] 24 | } 25 | ] 26 | } 27 | }, 28 | sql: { 29 | Text: 30 | select m.Id, 31 | m.RightName, 32 | s.ManyToManyLeftEntityId, 33 | s.ManyToManyRightEntityId, 34 | s.Id, 35 | s.LeftName 36 | from ManyToManyRightEntities as m 37 | left outer join 38 | (select m2.ManyToManyLeftEntityId, 39 | m2.ManyToManyRightEntityId, 40 | m3.Id, 41 | m3.LeftName 42 | from ManyToManyMiddleEntities as m2 43 | inner join 44 | ManyToManyLeftEntities as m3 45 | on m2.ManyToManyLeftEntityId = m3.Id) as s 46 | on m.Id = s.ManyToManyRightEntityId 47 | where exists (select 1 48 | from ManyToManyMiddleEntities as m0 49 | inner join 50 | ManyToManyLeftEntities as m1 51 | on m0.ManyToManyLeftEntityId = m1.Id 52 | where m.Id = m0.ManyToManyRightEntityId 53 | and (m1.LeftName <> N'Left2' 54 | or m1.LeftName is null)) 55 | order by m.Id, s.ManyToManyLeftEntityId, s.ManyToManyRightEntityId 56 | } 57 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.ManyToManyRightWhereAndInclude.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "manyToManyLeftEntities": [ 6 | { 7 | "leftName": "Left1", 8 | "rights": [ 9 | { 10 | "rightName": "Right1" 11 | }, 12 | { 13 | "rightName": "Right2" 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | }, 20 | sql: { 21 | Text: 22 | select m.Id, 23 | m.LeftName, 24 | s.ManyToManyLeftEntityId, 25 | s.ManyToManyRightEntityId, 26 | s.Id, 27 | s.RightName 28 | from ManyToManyLeftEntities as m 29 | left outer join 30 | (select m2.ManyToManyLeftEntityId, 31 | m2.ManyToManyRightEntityId, 32 | m3.Id, 33 | m3.RightName 34 | from ManyToManyMiddleEntities as m2 35 | inner join 36 | ManyToManyRightEntities as m3 37 | on m2.ManyToManyRightEntityId = m3.Id) as s 38 | on m.Id = s.ManyToManyLeftEntityId 39 | where exists (select 1 40 | from ManyToManyMiddleEntities as m0 41 | inner join 42 | ManyToManyRightEntities as m1 43 | on m0.ManyToManyRightEntityId = m1.Id 44 | where m.Id = m0.ManyToManyLeftEntityId 45 | and m1.RightName = N'Right2') 46 | order by m.Id, s.ManyToManyLeftEntityId, s.ManyToManyRightEntityId 47 | } 48 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.ManyToManyRightWhereAndInclude_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "manyToManyLeftEntities": [ 6 | { 7 | "leftName": "Left1", 8 | "rights": [ 9 | { 10 | "rightName": "Right1" 11 | }, 12 | { 13 | "rightName": "Right2" 14 | } 15 | ] 16 | }, 17 | { 18 | "leftName": "Left2", 19 | "rights": [ 20 | { 21 | "rightName": "Right1" 22 | } 23 | ] 24 | } 25 | ] 26 | } 27 | }, 28 | sql: { 29 | Text: 30 | select m.Id, 31 | m.LeftName, 32 | s.ManyToManyLeftEntityId, 33 | s.ManyToManyRightEntityId, 34 | s.Id, 35 | s.RightName 36 | from ManyToManyLeftEntities as m 37 | left outer join 38 | (select m2.ManyToManyLeftEntityId, 39 | m2.ManyToManyRightEntityId, 40 | m3.Id, 41 | m3.RightName 42 | from ManyToManyMiddleEntities as m2 43 | inner join 44 | ManyToManyRightEntities as m3 45 | on m2.ManyToManyRightEntityId = m3.Id) as s 46 | on m.Id = s.ManyToManyLeftEntityId 47 | where exists (select 1 48 | from ManyToManyMiddleEntities as m0 49 | inner join 50 | ManyToManyRightEntities as m1 51 | on m0.ManyToManyRightEntityId = m1.Id 52 | where m.Id = m0.ManyToManyLeftEntityId 53 | and (m1.RightName <> N'Right2' 54 | or m1.RightName is null)) 55 | order by m.Id, s.ManyToManyLeftEntityId, s.ManyToManyRightEntityId 56 | } 57 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Many_children.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "manyChildren": [ 6 | { 7 | "child1": { 8 | "id": "Guid_1" 9 | } 10 | } 11 | ] 12 | } 13 | }, 14 | sql: { 15 | Text: 16 | select w.Id, 17 | c.Id, 18 | c.ParentId, 19 | c0.Id, 20 | c0.ParentId 21 | from WithManyChildrenEntities as w 22 | left outer join 23 | Child2Entities as c 24 | on w.Id = c.ParentId 25 | left outer join 26 | Child1Entities as c0 27 | on w.Id = c0.ParentId 28 | } 29 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Multiple_nested.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "level1Entities": [ 6 | { 7 | "level2Entity": { 8 | "level3Entity": { 9 | "property": "Value" 10 | } 11 | } 12 | } 13 | ] 14 | } 15 | }, 16 | sql: { 17 | Text: 18 | select l.Id, 19 | l.Level2EntityId, 20 | l.Level2EntityId1, 21 | l0.Id, 22 | l0.Level3EntityId, 23 | l1.Id, 24 | l1.Property 25 | from Level1Entities as l 26 | left outer join 27 | Level2Entities as l0 28 | on l.Level2EntityId1 = l0.Id 29 | left outer join 30 | Level3Entities as l1 31 | on l0.Level3EntityId = l1.Id 32 | } 33 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Multiple_nested_Filtered.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "level1Entities": [ 6 | { 7 | "level2Entity": { 8 | "level3Entity": null 9 | } 10 | } 11 | ] 12 | } 13 | }, 14 | sql: { 15 | Text: 16 | select l.Id, 17 | l.Level2EntityId, 18 | l.Level2EntityId1, 19 | l0.Id, 20 | l0.Level3EntityId, 21 | l1.Id, 22 | l1.Property 23 | from Level1Entities as l 24 | left outer join 25 | Level2Entities as l0 26 | on l.Level2EntityId1 = l0.Id 27 | left outer join 28 | Level3Entities as l1 29 | on l0.Level3EntityId = l1.Id 30 | } 31 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.NamedId.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "namedEntities": [ 6 | { 7 | "property": "Value1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select n.NamedId, 15 | n.Property 16 | from NamedEntities as n 17 | where n.NamedId = 'Guid_1' 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Null_on_nested.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "level1Entities": [ 6 | { 7 | "level2Entity": { 8 | "level3Entity": { 9 | "property": "Valuea" 10 | } 11 | } 12 | } 13 | ] 14 | } 15 | }, 16 | sql: { 17 | Text: 18 | select l.Id, 19 | l.Level2EntityId, 20 | l.Level2EntityId1, 21 | l0.Id, 22 | l0.Level3EntityId, 23 | l1.Id, 24 | l1.Property 25 | from Level1Entities as l 26 | left outer join 27 | Level2Entities as l0 28 | on l.Level2EntityId1 = l0.Id 29 | left outer join 30 | Level3Entities as l1 31 | on l0.Level3EntityId = l1.Id 32 | where l0.Level3EntityId = 'Guid_1' 33 | } 34 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Null_on_nested_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "level1Entities": [ 6 | { 7 | "level2Entity": { 8 | "level3Entity": null 9 | } 10 | } 11 | ] 12 | } 13 | }, 14 | sql: { 15 | Text: 16 | select l.Id, 17 | l.Level2EntityId, 18 | l.Level2EntityId1, 19 | l0.Id, 20 | l0.Level3EntityId, 21 | l1.Id, 22 | l1.Property 23 | from Level1Entities as l 24 | left outer join 25 | Level2Entities as l0 26 | on l.Level2EntityId1 = l0.Id 27 | left outer join 28 | Level3Entities as l1 29 | on l0.Level3EntityId = l1.Id 30 | where l0.Level3EntityId <> 'Guid_1' 31 | or l0.Level3EntityId is null 32 | } 33 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.OrderBy.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value1" 8 | }, 9 | { 10 | "property": "Value2" 11 | } 12 | ] 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select p.Id, 18 | p.Property 19 | from ParentEntities as p 20 | order by p.Property 21 | } 22 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.OrderByDescending.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value2" 8 | }, 9 | { 10 | "property": "Value1" 11 | } 12 | ] 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select p.Id, 18 | p.Property 19 | from ParentEntities as p 20 | order by p.Property desc 21 | } 22 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.OrderByNested.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "childEntities": [ 6 | { 7 | "property": "Value2" 8 | }, 9 | { 10 | "property": "Value4" 11 | } 12 | ] 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select c.Id, 18 | c.Nullable, 19 | c.ParentId, 20 | c.Property 21 | from ChildEntities as c 22 | left outer join 23 | ParentEntities as p 24 | on c.ParentId = p.Id 25 | order by p.Property 26 | } 27 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.OrderByNestedNullable.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "childEntities": [ 6 | { 7 | "property": "Value3" 8 | }, 9 | { 10 | "property": "Value2" 11 | } 12 | ] 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select c.Id, 18 | c.Nullable, 19 | c.ParentId, 20 | c.Property 21 | from ChildEntities as c 22 | left outer join 23 | ParentEntities as p 24 | on c.ParentId = p.Id 25 | order by p.Property 26 | } 27 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.OrderByNullable.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": null 8 | }, 9 | { 10 | "property": "Value1" 11 | } 12 | ] 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select p.Id, 18 | p.Property 19 | from ParentEntities as p 20 | order by p.Property 21 | } 22 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Owned.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "ownedParent": { 6 | "property": "Parent value", 7 | "child1": { 8 | "property": "Value1" 9 | } 10 | } 11 | } 12 | }, 13 | sql: { 14 | Text: 15 | select top (2) o.Id, 16 | o.Property, 17 | o.Child1_Property, 18 | o.Child2_Property 19 | from OwnedParents as o 20 | where o.Id = 'Guid_1' 21 | } 22 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Parent_child_with_id.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value1", 8 | "children": [ 9 | { 10 | "property": "Child1" 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | }, 17 | sql: { 18 | Text: 19 | select p.Id, 20 | p.Property, 21 | c.Id, 22 | c.Nullable, 23 | c.ParentId, 24 | c.Property 25 | from ParentEntities as p 26 | left outer join 27 | ChildEntities as c 28 | on p.Id = c.ParentId 29 | order by p.Id 30 | } 31 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Parent_with_id_child_with_id.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value1", 8 | "children": [ 9 | { 10 | "property": "Child1" 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | }, 17 | sql: { 18 | Text: 19 | select p.Id, 20 | p.Property, 21 | c.Id, 22 | c.Nullable, 23 | c.ParentId, 24 | c.Property 25 | from ParentEntities as p 26 | left outer join 27 | ChildEntities as c 28 | on p.Id = c.ParentId 29 | where p.Id = 'Guid_1' 30 | order by p.Id 31 | } 32 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Query_Cyclic.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "childEntities": [ 6 | { 7 | "property": "Value2", 8 | "parent": { 9 | "property": "Value1", 10 | "children": [ 11 | { 12 | "property": "Value2", 13 | "parent": { 14 | "property": "Value1" 15 | } 16 | }, 17 | { 18 | "property": "Value3", 19 | "parent": { 20 | "property": "Value1" 21 | } 22 | } 23 | ] 24 | } 25 | }, 26 | { 27 | "property": "Value3", 28 | "parent": { 29 | "property": "Value1", 30 | "children": [ 31 | { 32 | "property": "Value2", 33 | "parent": { 34 | "property": "Value1" 35 | } 36 | }, 37 | { 38 | "property": "Value3", 39 | "parent": { 40 | "property": "Value1" 41 | } 42 | } 43 | ] 44 | } 45 | }, 46 | { 47 | "property": "Value5", 48 | "parent": { 49 | "property": "Value4", 50 | "children": [ 51 | { 52 | "property": "Value5", 53 | "parent": { 54 | "property": "Value4" 55 | } 56 | } 57 | ] 58 | } 59 | } 60 | ] 61 | } 62 | }, 63 | sql: { 64 | Text: 65 | select c.Id, 66 | c.Nullable, 67 | c.ParentId, 68 | c.Property, 69 | p.Id, 70 | p.Property, 71 | c0.Id, 72 | c0.Nullable, 73 | c0.ParentId, 74 | c0.Property 75 | from ChildEntities as c 76 | left outer join 77 | ParentEntities as p 78 | on c.ParentId = p.Id 79 | left outer join 80 | ChildEntities as c0 81 | on p.Id = c0.ParentId 82 | order by c.Property, c.Id, p.Id 83 | } 84 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Query_Large_Text_NoAsync.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "childEntities": [ 6 | { 7 | "parent": { 8 | "property": "Value1" 9 | } 10 | }, 11 | { 12 | "parent": { 13 | "property": "Value1" 14 | } 15 | }, 16 | { 17 | "parent": { 18 | "property": "Value4" 19 | } 20 | } 21 | ] 22 | } 23 | }, 24 | sql: { 25 | Text: 26 | select c.Id, 27 | c.Nullable, 28 | c.ParentId, 29 | c.Property, 30 | p.Id, 31 | p.Property 32 | from ChildEntities as c 33 | left outer join 34 | ParentEntities as p 35 | on c.ParentId = p.Id 36 | order by c.Property 37 | } 38 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Query_NoTracking.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "childEntities": [ 6 | { 7 | "property": "Value2", 8 | "parent": { 9 | "property": "Value1" 10 | } 11 | }, 12 | { 13 | "property": "Value3", 14 | "parent": { 15 | "property": "Value1" 16 | } 17 | }, 18 | { 19 | "property": "Value5", 20 | "parent": { 21 | "property": "Value4" 22 | } 23 | } 24 | ] 25 | } 26 | }, 27 | sql: { 28 | Text: 29 | select c.Id, 30 | c.Nullable, 31 | c.ParentId, 32 | c.Property, 33 | p.Id, 34 | p.Property 35 | from ChildEntities as c 36 | left outer join 37 | ParentEntities as p 38 | on c.ParentId = p.Id 39 | order by c.Property 40 | } 41 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.RootList_filtered.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntitiesFiltered": [ 6 | { 7 | "property": "Value1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select f.Id, 15 | f.Property 16 | from FilterParentEntities as f 17 | } 18 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Root_connectionFiltered.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntitiesConnectionFiltered": { 6 | "totalCount": 2, 7 | "edges": [ 8 | { 9 | "cursor": "0", 10 | "node": { 11 | "property": "Value1" 12 | } 13 | } 14 | ], 15 | "items": [ 16 | { 17 | "property": "Value1" 18 | } 19 | ] 20 | } 21 | } 22 | }, 23 | sql: [ 24 | { 25 | Text: 26 | select COUNT(*) 27 | from FilterParentEntities as f 28 | }, 29 | { 30 | Text: 31 | select f.Id, 32 | f.Property 33 | from FilterParentEntities as f 34 | order by f.Id 35 | offset @__p_0 rows fetch next @__p_1 rows only, 36 | Parameters: { 37 | @__p_0: 0, 38 | @__p_1: 10 39 | } 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.SingleNullable_Found.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityNullable": { 6 | "property": "Value1" 7 | } 8 | } 9 | }, 10 | sql: { 11 | Text: 12 | select top (2) p.Id, 13 | p.Property 14 | from ParentEntities as p 15 | where p.Id = 'Guid_1' 16 | } 17 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.SingleNullable_NotFound.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityNullable": null 6 | } 7 | }, 8 | sql: { 9 | Text: 10 | select top (2) p.Id, 11 | p.Property 12 | from ParentEntities as p 13 | where p.Id = 'Guid_1' 14 | } 15 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.SingleParent_Child.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntity": { 6 | "property": "Value1", 7 | "children": [ 8 | { 9 | "property": "Value2" 10 | }, 11 | { 12 | "property": "Value3" 13 | } 14 | ] 15 | } 16 | } 17 | }, 18 | sql: { 19 | Text: 20 | select p0.Id, 21 | p0.Property, 22 | c.Id, 23 | c.Nullable, 24 | c.ParentId, 25 | c.Property 26 | from (select top (2) p.Id, 27 | p.Property 28 | from ParentEntities as p 29 | where p.Id = 'Guid_1') as p0 30 | left outer join 31 | ChildEntities as c 32 | on p0.Id = c.ParentId 33 | order by p0.Id 34 | } 35 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.SingleParent_Child_WithFragment.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntity": { 6 | "property": "Value1", 7 | "children": [ 8 | { 9 | "property": "Value2" 10 | }, 11 | { 12 | "property": "Value3" 13 | } 14 | ] 15 | } 16 | } 17 | }, 18 | sql: { 19 | Text: 20 | select p0.Id, 21 | p0.Property, 22 | c.Id, 23 | c.Nullable, 24 | c.ParentId, 25 | c.Property 26 | from (select top (2) p.Id, 27 | p.Property 28 | from ParentEntities as p 29 | where p.Id = 'Guid_1') as p0 30 | left outer join 31 | ChildEntities as c 32 | on p0.Id = c.ParentId 33 | order by p0.Id 34 | } 35 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.SingleParent_Child_mutation.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityMutation": { 6 | "property": "Foo", 7 | "children": [ 8 | { 9 | "property": "Value2" 10 | }, 11 | { 12 | "property": "Value3" 13 | } 14 | ] 15 | } 16 | } 17 | }, 18 | sql: [ 19 | { 20 | Text: 21 | select p0.Id, 22 | p0.Property, 23 | c.Id, 24 | c.Nullable, 25 | c.ParentId, 26 | c.Property 27 | from (select top (2) p.Id, 28 | p.Property 29 | from ParentEntities as p 30 | where p.Id = 'Guid_1') as p0 31 | left outer join 32 | ChildEntities as c 33 | on p0.Id = c.ParentId 34 | order by p0.Id 35 | }, 36 | { 37 | Text: 38 | set implicit_transactions off; 39 | 40 | set nocount on; 41 | 42 | update ParentEntities 43 | set Property = @p0 44 | output 1 45 | where Id = @p1, 46 | Parameters: { 47 | @p1: Guid_1, 48 | @p0: { 49 | Value: Foo, 50 | Size: 4000, 51 | IsNullable: true 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Single_Found.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntity": { 6 | "property": "Value1" 7 | } 8 | } 9 | }, 10 | sql: { 11 | Text: 12 | select top (2) p.Id, 13 | p.Property 14 | from ParentEntities as p 15 | where p.Id = 'Guid_1' 16 | } 17 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Single_Found_Large_Text_NoAsync.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntity": { 6 | "id": "Guid_1" 7 | } 8 | } 9 | }, 10 | sql: { 11 | Text: 12 | select top (2) p.Id, 13 | p.Property 14 | from ParentEntities as p 15 | where p.Id = 'Guid_1' 16 | } 17 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Single_Found_NoTracking.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntity": { 6 | "property": "Value1" 7 | } 8 | } 9 | }, 10 | sql: { 11 | Text: 12 | select top (2) p.Id, 13 | p.Property 14 | from ParentEntities as p 15 | where p.Id = 'Guid_1' 16 | } 17 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Single_IdOnly.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityIdOnly": { 6 | "property": "Value1", 7 | "children": [ 8 | { 9 | "property": "Value2" 10 | } 11 | ] 12 | } 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select p0.Id, 18 | p0.Property, 19 | c.Id, 20 | c.Nullable, 21 | c.ParentId, 22 | c.Property 23 | from (select top (2) p.Id, 24 | p.Property 25 | from ParentEntities as p 26 | where p.Id = 'Guid_1') as p0 27 | left outer join 28 | ChildEntities as c 29 | on p0.Id = c.ParentId 30 | order by p0.Id 31 | } 32 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Single_NoArgs.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntityWithNoArgs": { 6 | "property": "Value1", 7 | "children": [ 8 | { 9 | "property": "Value2" 10 | } 11 | ] 12 | } 13 | } 14 | }, 15 | sql: { 16 | Text: 17 | select p0.Id, 18 | p0.Property, 19 | c.Id, 20 | c.Nullable, 21 | c.ParentId, 22 | c.Property 23 | from (select top (2) p.Id, 24 | p.Property 25 | from ParentEntities as p 26 | where p.Id = 'Guid_1') as p0 27 | left outer join 28 | ChildEntities as c 29 | on p0.Id = c.ParentId 30 | order by p0.Id 31 | } 32 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Single_NotFound.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | Type: SingleEntityNotFoundException, 4 | Message: Not found 5 | }, 6 | sql: { 7 | Text: 8 | select top (2) p.Id, 9 | p.Property 10 | from ParentEntities as p 11 | where p.Id = '00000000-0000-0000-0000-000000000001' 12 | } 13 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Skip.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value2" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | order by p.Property 18 | offset @__p_0 rows, 19 | Parameters: { 20 | @__p_0: 1 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.SkipNoOrder.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: An error was generated for warning 'Microsoft.EntityFrameworkCore.Query.RowLimitingOperationWithoutOrderByWarning': The query uses a row limiting operator ('Skip'/'Take') without an 'OrderBy' operator. This may lead to unpredictable results. If the 'Distinct' operator is used after 'OrderBy', then make sure to use the 'OrderBy' operator after 'Distinct' as the ordering would otherwise get erased. This exception can be suppressed or logged by passing event ID 'CoreEventId.RowLimitingOperationWithoutOrderByWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'. 4 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Take.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select top (@__p_0) p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | order by p.Property, 18 | Parameters: { 19 | @__p_0: 1 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.TakeNoOrder.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: An error was generated for warning 'Microsoft.EntityFrameworkCore.Query.RowLimitingOperationWithoutOrderByWarning': The query uses a row limiting operator ('Skip'/'Take') without an 'OrderBy' operator. This may lead to unpredictable results. If the 'Distinct' operator is used after 'OrderBy', then make sure to use the 'OrderBy' operator after 'Distinct' as the ordering would otherwise get erased. This exception can be suppressed or logged by passing event ID 'CoreEventId.RowLimitingOperationWithoutOrderByWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'. 4 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value2" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property = N'value2' 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_date.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "dateEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select d.Id, 15 | d.Property 16 | from DateEntities as d 17 | where d.Property = '2020-10-01' 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_date_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "dateEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select d.Id, 15 | d.Property 16 | from DateEntities as d 17 | where d.Property <> '2020-10-01' 18 | or d.Property is null 19 | } 20 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_default_comparison.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value2" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property = N'value2' 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_enum.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "enumEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select e.Id, 15 | e.Property 16 | from EnumEntities as e 17 | where e.Property = 4 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_enum_in.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "enumEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select e.Id, 15 | e.Property 16 | from EnumEntities as e 17 | where e.Property = 4 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_enum_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "enumEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select e.Id, 15 | e.Property 16 | from EnumEntities as e 17 | where e.Property <> 4 18 | or e.Property is null 19 | } 20 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_multiple.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value3" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property is not null 18 | and p.Property like N'Valu%' 19 | and p.Property like N'%ue3' 20 | } 21 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property <> N'value2' 18 | or p.Property is null 19 | } 20 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_null_comparison_value.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property is null 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_null_comparison_value_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property is not null 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_string_contains.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "stringEntities": [ 6 | { 7 | "id": "Guid_1", 8 | "property": "ab" 9 | }, 10 | { 11 | "id": "Guid_2", 12 | "property": "abc" 13 | }, 14 | { 15 | "id": "Guid_3", 16 | "property": "b" 17 | }, 18 | { 19 | "id": "Guid_4", 20 | "property": "bc" 21 | } 22 | ] 23 | } 24 | }, 25 | sql: { 26 | Text: 27 | select s.Id, 28 | s.Property 29 | from StringEntities as s 30 | where s.Property is not null 31 | and cast (CHARINDEX(N'b', s.Property) as int) - 1 <> -1 32 | order by s.Property 33 | } 34 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_string_contains_diffCase.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "stringEntities": [ 6 | { 7 | "id": "Guid_1", 8 | "property": "ab" 9 | }, 10 | { 11 | "id": "Guid_2", 12 | "property": "abc" 13 | }, 14 | { 15 | "id": "Guid_3", 16 | "property": "b" 17 | }, 18 | { 19 | "id": "Guid_4", 20 | "property": "bc" 21 | } 22 | ] 23 | } 24 | }, 25 | sql: { 26 | Text: 27 | select s.Id, 28 | s.Property 29 | from StringEntities as s 30 | where s.Property is not null 31 | and cast (CHARINDEX(N'B', s.Property) as int) - 1 <> -1 32 | order by s.Property 33 | } 34 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_string_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "stringEntities": [ 6 | { 7 | "id": "Guid_1", 8 | "property": null 9 | }, 10 | { 11 | "id": "Guid_2", 12 | "property": "Value" 13 | } 14 | ] 15 | } 16 | }, 17 | sql: { 18 | Text: 19 | select s.Id, 20 | s.Property 21 | from StringEntities as s 22 | where s.Property <> N'notValue' 23 | or s.Property is null 24 | order by s.Property 25 | } 26 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_string_notEqual_diffCase.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "stringEntities": [ 6 | { 7 | "id": "Guid_1", 8 | "property": null 9 | }, 10 | { 11 | "id": "Guid_2", 12 | "property": "Value" 13 | } 14 | ] 15 | } 16 | }, 17 | sql: { 18 | Text: 19 | select s.Id, 20 | s.Property 21 | from StringEntities as s 22 | where s.Property <> N'NotValue' 23 | or s.Property is null 24 | order by s.Property 25 | } 26 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_time.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "timeEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select t.Id, 15 | t.Property 16 | from TimeEntities as t 17 | where t.Property = '10:11:00' 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_time_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "timeEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select t.Id, 15 | t.Property 16 | from TimeEntities as t 17 | where t.Property <> '10:11:00' 18 | or t.Property is null 19 | } 20 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_with_nullable_properties1.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "withNullableEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select w.Id, 15 | w.Nullable 16 | from WithNullableEntities as w 17 | where w.Nullable is null 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_with_nullable_properties1_NotEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "withNullableEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select w.Id, 15 | w.Nullable 16 | from WithNullableEntities as w 17 | where w.Nullable is not null 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_with_nullable_properties2.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "withNullableEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select w.Id, 15 | w.Nullable 16 | from WithNullableEntities as w 17 | where w.Nullable = 10 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_with_nullable_properties2_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "withNullableEntities": [ 6 | { 7 | "id": "Guid_1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select w.Id, 15 | w.Nullable 16 | from WithNullableEntities as w 17 | where w.Nullable <> 10 18 | or w.Nullable is null 19 | } 20 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_with_variable.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value2" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property = N'value2' 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.Where_with_variable_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "parentEntities": [ 6 | { 7 | "property": "Value1" 8 | } 9 | ] 10 | } 11 | }, 12 | sql: { 13 | Text: 14 | select p.Id, 15 | p.Property 16 | from ParentEntities as p 17 | where p.Property <> N'value2' 18 | or p.Property is null 19 | } 20 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.With_null_navigation_property.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "childEntities": [ 6 | { 7 | "property": "Value2", 8 | "parent": { 9 | "property": "Value1" 10 | } 11 | }, 12 | { 13 | "property": "Value3", 14 | "parent": { 15 | "property": "Value1" 16 | } 17 | } 18 | ] 19 | } 20 | }, 21 | sql: { 22 | Text: 23 | select c.Id, 24 | c.Nullable, 25 | c.ParentId, 26 | c.Property, 27 | p.Id, 28 | p.Property 29 | from ChildEntities as c 30 | left outer join 31 | ParentEntities as p 32 | on c.ParentId = p.Id 33 | where c.ParentId = 'Guid_1' 34 | order by c.Property 35 | } 36 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/IntegrationTests.With_null_navigation_property_notEqual.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: 3 | { 4 | "data": { 5 | "childEntities": [ 6 | { 7 | "property": "Value5", 8 | "parent": null 9 | } 10 | ] 11 | } 12 | }, 13 | sql: { 14 | Text: 15 | select c.Id, 16 | c.Nullable, 17 | c.ParentId, 18 | c.Property, 19 | p.Id, 20 | p.Property 21 | from ChildEntities as c 22 | left outer join 23 | ParentEntities as p 24 | on c.ParentId = p.Id 25 | where c.ParentId <> 'Guid_1' 26 | or c.ParentId is null 27 | order by c.Property 28 | } 29 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/Mutation.cs: -------------------------------------------------------------------------------- 1 | public class Mutation : 2 | QueryGraphType 3 | { 4 | public Mutation(IEfGraphQLService efGraphQlService) : 5 | base(efGraphQlService) => 6 | AddSingleField( 7 | name: "parentEntityMutation", 8 | resolve: _ => _.DbContext.ParentEntities, 9 | mutate: (context, entity) => 10 | { 11 | entity.Property = "Foo"; 12 | return context.DbContext.SaveChangesAsync(); 13 | }); 14 | } -------------------------------------------------------------------------------- /src/Tests/IntegrationTests/QueryExecutor.cs: -------------------------------------------------------------------------------- 1 | static class QueryExecutor 2 | { 3 | public static async Task ExecuteQuery( 4 | string query, 5 | ServiceCollection services, 6 | TDbContext data, 7 | Inputs? inputs, 8 | Filters? filters, 9 | bool disableTracking, 10 | bool disableAsync) 11 | where TDbContext : DbContext 12 | { 13 | EfGraphQLConventions.RegisterInContainer( 14 | services, 15 | (_, _) => data, 16 | data.Model, 17 | _ => filters, 18 | disableTracking, 19 | disableAsync); 20 | await using var provider = services.BuildServiceProvider(); 21 | using var schema = new Schema(provider); 22 | var executer = new EfDocumentExecuter(); 23 | 24 | var options = new ExecutionOptions 25 | { 26 | Schema = schema, 27 | Query = query, 28 | ThrowOnUnhandledException = true, 29 | Variables = inputs, 30 | RequestServices = provider, 31 | }; 32 | 33 | var result = await executer.ExecuteWithErrorCheck(options); 34 | return await result.Serialize(); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingChild.cs: -------------------------------------------------------------------------------- 1 | [Table("child")] 2 | public class MappingChild 3 | { 4 | public Guid Id { get; set; } = Guid.NewGuid(); 5 | public string? Property { get; set; } 6 | public Guid ParentId { get; set; } 7 | public MappingParent Parent { get; set; } = null!; 8 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingChildGraphType.cs: -------------------------------------------------------------------------------- 1 | public class MappingChildGraphType : 2 | EfObjectGraphType 3 | { 4 | public MappingChildGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingContext.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | public class MappingContext(DbContextOptions options) : 4 | DbContext(options) 5 | { 6 | public DbSet Parents { get; set; } = null!; 7 | public DbSet Children { get; set; } = null!; 8 | 9 | protected override void OnModelCreating(ModelBuilder modelBuilder) 10 | { 11 | var parentBuilder = modelBuilder.Entity(); 12 | parentBuilder.Property(_ => _.JsonProperty) 13 | .HasConversion( 14 | v => JsonConvert.SerializeObject(v), 15 | v => JsonConvert.DeserializeObject>(v)!); 16 | 17 | modelBuilder.Entity(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingParent.cs: -------------------------------------------------------------------------------- 1 | [Table("parent")] 2 | public class MappingParent 3 | { 4 | public Guid Id { get; set; } = Guid.NewGuid(); 5 | public string? Property { get; set; } 6 | public IList Children { get; set; } = []; 7 | public IList JsonProperty { get; set; } = []; 8 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingParentGraphType.cs: -------------------------------------------------------------------------------- 1 | public class MappingParentGraphType : 2 | EfObjectGraphType 3 | { 4 | public MappingParentGraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingQuery.cs: -------------------------------------------------------------------------------- 1 | public class MappingQuery : 2 | ObjectGraphType 3 | { 4 | public MappingQuery( 5 | IEfGraphQLService graphQlService) 6 | { 7 | graphQlService.AddSingleField( 8 | graph: this, 9 | name: "child", 10 | resolve: _ => _.DbContext.Children); 11 | graphQlService.AddFirstField( 12 | graph: this, 13 | name: "childFirst", 14 | resolve: _ => _.DbContext.Children); 15 | graphQlService.AddQueryField( 16 | graph: this, 17 | name: "children", 18 | resolve: _ => _.DbContext.Children); 19 | graphQlService.AddQueryField( 20 | graph: this, 21 | name: "childrenOmitQueryArguments", 22 | resolve: _ => _.DbContext.Children, 23 | omitQueryArguments: true); 24 | graphQlService.AddSingleField( 25 | graph: this, 26 | name: "parent", 27 | resolve: _ => _.DbContext.Parents); 28 | graphQlService.AddFirstField( 29 | graph: this, 30 | name: "parentFirst", 31 | resolve: _ => _.DbContext.Parents); 32 | graphQlService.AddQueryField( 33 | graph: this, 34 | name: "parents", 35 | resolve: _ => _.DbContext.Parents); 36 | } 37 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingSchema.cs: -------------------------------------------------------------------------------- 1 | public class MappingSchema : 2 | GraphQL.Types.Schema 3 | { 4 | public MappingSchema(EfGraphQLService graphQlService, IServiceProvider provider) : 5 | base(provider) 6 | { 7 | Query = new MappingQuery(graphQlService); 8 | RegisterTypeMapping(typeof(MappingParent), typeof(MappingParentGraphType)); 9 | RegisterTypeMapping(typeof(MappingChild), typeof(MappingChildGraphType)); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingTests.NavigationProperty.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | expression: context => context.Source.Parent, 3 | result: { 4 | Id: Guid_1, 5 | Property: value, 6 | Children: [ 7 | { 8 | Id: Guid_2, 9 | ParentId: Guid_1 10 | } 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingTests.PropertyToObject.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | expression: source => Convert(source.Property, Object), 3 | result: value 4 | } -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingTests.Resolve.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Id: Guid_1, 4 | ParentId: Guid_2 5 | } 6 | ] -------------------------------------------------------------------------------- /src/Tests/Mapping/MappingTests.SchemaPrint.verified.txt: -------------------------------------------------------------------------------- 1 | schema { 2 | query: MappingQuery 3 | } 4 | 5 | type MappingQuery { 6 | child(id: ID, ids: [ID!], where: [WhereExpression!]): MappingChild! 7 | childFirst(id: ID, ids: [ID!], where: [WhereExpression!]): MappingChild! 8 | children(id: ID, ids: [ID!], where: [WhereExpression!], orderBy: [OrderBy!], skip: Int, take: Int): [MappingChild!]! 9 | childrenOmitQueryArguments: [MappingChild!]! 10 | parent(id: ID, ids: [ID!], where: [WhereExpression!]): MappingParent! 11 | parentFirst(id: ID, ids: [ID!], where: [WhereExpression!]): MappingParent! 12 | parents(id: ID, ids: [ID!], where: [WhereExpression!], orderBy: [OrderBy!], skip: Int, take: Int): [MappingParent!]! 13 | } 14 | 15 | type MappingChild { 16 | parent: MappingParent! 17 | id: ID! 18 | parentId: ID! 19 | property: String 20 | } 21 | 22 | type MappingParent { 23 | children(id: ID, ids: [ID!], where: [WhereExpression!], orderBy: [OrderBy!], skip: Int, take: Int): [MappingChild!]! 24 | id: ID! 25 | jsonProperty: [String!]! 26 | property: String 27 | } 28 | 29 | input WhereExpression { 30 | path: String 31 | comparison: Comparison 32 | negate: Boolean 33 | value: [String] 34 | connector: Connector 35 | groupedExpressions: [WhereExpression] 36 | } 37 | 38 | enum Comparison { 39 | contains 40 | endsWith 41 | equal 42 | notEqual 43 | greaterThan 44 | greaterThanOrEqual 45 | "Negation Property used with the 'in' comparison should be used in place of this" 46 | notIn 47 | in 48 | lessThan 49 | lessThanOrEqual 50 | like 51 | startsWith 52 | } 53 | 54 | enum Connector { 55 | and 56 | or 57 | } 58 | 59 | input OrderBy { 60 | path: String! 61 | descending: Boolean 62 | } 63 | -------------------------------------------------------------------------------- /src/Tests/ModuleInitializer.cs: -------------------------------------------------------------------------------- 1 | public static class ModuleInitializer 2 | { 3 | [ModuleInitializer] 4 | public static void Init() 5 | { 6 | VerifySqlServer.Initialize(); 7 | VerifierSettings.IgnoreMember("HasTransaction"); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Tests/MultiContextTests/DbContext1.cs: -------------------------------------------------------------------------------- 1 | public class DbContext1(DbContextOptions options) : 2 | DbContext(options) 3 | { 4 | public DbSet Entities { get; set; } = null!; 5 | 6 | protected override void OnModelCreating(ModelBuilder modelBuilder) => 7 | modelBuilder.Entity(); 8 | } -------------------------------------------------------------------------------- /src/Tests/MultiContextTests/DbContext2.cs: -------------------------------------------------------------------------------- 1 | public class DbContext2(DbContextOptions options) : 2 | DbContext(options) 3 | { 4 | public DbSet Entities { get; set; } = null!; 5 | 6 | protected override void OnModelCreating(ModelBuilder modelBuilder) => 7 | modelBuilder.Entity(); 8 | } -------------------------------------------------------------------------------- /src/Tests/MultiContextTests/Graphs/Entity1.cs: -------------------------------------------------------------------------------- 1 | [Table("entity1")] 2 | public class Entity1 3 | { 4 | public Guid Id { get; set; } = Guid.NewGuid(); 5 | public string? Property { get; set; } 6 | } -------------------------------------------------------------------------------- /src/Tests/MultiContextTests/Graphs/Entity1GraphType.cs: -------------------------------------------------------------------------------- 1 | public class Entity1GraphType : 2 | EfObjectGraphType 3 | { 4 | public Entity1GraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/MultiContextTests/Graphs/Entity2.cs: -------------------------------------------------------------------------------- 1 | [Table("entity2")] 2 | public class Entity2 3 | { 4 | public Guid Id { get; set; } = Guid.NewGuid(); 5 | public string? Property { get; set; } 6 | } -------------------------------------------------------------------------------- /src/Tests/MultiContextTests/Graphs/Entity2GraphType.cs: -------------------------------------------------------------------------------- 1 | public class Entity2GraphType : 2 | EfObjectGraphType 3 | { 4 | public Entity2GraphType(IEfGraphQLService graphQlService) : 5 | base(graphQlService) => 6 | AutoMap(); 7 | } -------------------------------------------------------------------------------- /src/Tests/MultiContextTests/MultiContextQuery.cs: -------------------------------------------------------------------------------- 1 | public class MultiContextQuery : 2 | ObjectGraphType 3 | { 4 | public MultiContextQuery( 5 | IEfGraphQLService efGraphQlService1, 6 | IEfGraphQLService efGraphQlService2) 7 | { 8 | efGraphQlService1.AddSingleField( 9 | graph: this, 10 | name: "entity1", 11 | resolve: _ => _.DbContext.Entities); 12 | efGraphQlService1.AddFirstField( 13 | graph: this, 14 | name: "entity1First", 15 | resolve: _ => _.DbContext.Entities); 16 | efGraphQlService2.AddSingleField( 17 | graph: this, 18 | name: "entity2", 19 | resolve: _ => _.DbContext.Entities); 20 | efGraphQlService2.AddFirstField( 21 | graph: this, 22 | name: "entity2First", 23 | resolve: _ => _.DbContext.Entities); 24 | } 25 | } -------------------------------------------------------------------------------- /src/Tests/MultiContextTests/MultiContextSchema.cs: -------------------------------------------------------------------------------- 1 | public class MultiContextSchema : 2 | GraphQL.Types.Schema 3 | { 4 | public MultiContextSchema(IServiceProvider provider) : 5 | base(provider) 6 | { 7 | RegisterTypeMapping(typeof(Entity1), typeof(Entity1GraphType)); 8 | RegisterTypeMapping(typeof(Entity2), typeof(Entity2GraphType)); 9 | Query = (MultiContextQuery)provider.GetService(typeof(MultiContextQuery))!; 10 | } 11 | } -------------------------------------------------------------------------------- /src/Tests/MultiContextTests/MultiContextTests.Run.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "entity1": { 4 | "property": "the entity1" 5 | }, 6 | "entity2": { 7 | "property": "the entity2" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Tests/PropertyCacheTests.cs: -------------------------------------------------------------------------------- 1 | public class PropertyCacheTests 2 | { 3 | [Fact] 4 | public void Property() 5 | { 6 | var target = new TargetForProperty 7 | { 8 | Member = "Value1" 9 | }; 10 | 11 | var property = PropertyCache.GetProperty("Member"); 12 | var result = property.Func(target); 13 | Assert.Equal("Value1", result); 14 | } 15 | 16 | public class TargetForProperty 17 | { 18 | public string? Member; 19 | } 20 | 21 | [Fact] 22 | public void PropertyNested() 23 | { 24 | var target = new TargetForPropertyNested 25 | { 26 | Child = new() 27 | { 28 | Member = "Value1" 29 | } 30 | }; 31 | 32 | var property = PropertyCache.GetProperty("Child.Member"); 33 | var result = property.Func(target); 34 | Assert.Equal("Value1", result); 35 | } 36 | 37 | public class TargetForPropertyNested 38 | { 39 | public TargetChildForPropertyNested? Child; 40 | } 41 | 42 | public class TargetChildForPropertyNested 43 | { 44 | public string? Member; 45 | } 46 | } -------------------------------------------------------------------------------- /src/Tests/ResultSerializer.cs: -------------------------------------------------------------------------------- 1 | static class ResultSerializer 2 | { 3 | static GraphQLSerializer writer = new(true); 4 | 5 | public static Task Serialize(this ExecutionResult result) 6 | { 7 | var stream = new MemoryStream(); 8 | writer.WriteAsync(stream,result); 9 | stream.Position = 0; 10 | var reader = new StreamReader(stream); 11 | return reader.ReadToEndAsync(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | $(NoWarn);EF1000;xUnit1051 5 | Exe 6 | testing 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/Tests/TypeConverterTests.cs: -------------------------------------------------------------------------------- 1 | public class TypeConverterTests 2 | { 3 | [Theory] 4 | [InlineData(typeof(int), "12", 12)] 5 | [InlineData(typeof(int?), null, null)] 6 | public void ConvertStringToType(Type type, string? value, object? expected) 7 | { 8 | var result = TypeConverter.ConvertStringToType(value, type); 9 | Assert.Equal(expected, result); 10 | } 11 | 12 | [Fact] 13 | public void ConvertStringToGuid() 14 | { 15 | var guid = Guid.NewGuid(); 16 | var value = guid.ToString(); 17 | var result = TypeConverter.ConvertStringToType(value, typeof(Guid)); 18 | Assert.Equal(guid, result); 19 | } 20 | 21 | [Fact] 22 | public void ConvertStringToDatetime() 23 | { 24 | var dateTime = DateTime.UtcNow.Date; 25 | var value = dateTime.ToString("o"); 26 | var result = TypeConverter.ConvertStringToType(value, typeof(DateTime)); 27 | Assert.Equal(dateTime, result); 28 | } 29 | 30 | [Fact] 31 | public void ConvertStringToDate() 32 | { 33 | var date = new Date(2020,10,1); 34 | var result = TypeConverter.ConvertStringToType(date.ToString("yyyy-MM-dd"), typeof(Date)); 35 | Assert.Equal(date, result); 36 | } 37 | 38 | [Fact] 39 | public void ConvertStringToTime() 40 | { 41 | var time = new Time(10,1); 42 | var result = TypeConverter.ConvertStringToType(time.ToString(), typeof(Time)); 43 | Assert.Equal(time, result); 44 | } 45 | 46 | [Fact] 47 | public void ConvertStringToEnum() 48 | { 49 | var day = DayOfWeek.Thursday; 50 | var value = day.ToString(); 51 | var result = TypeConverter.ConvertStringToType(value, typeof(DayOfWeek)); 52 | Assert.Equal(day, result); 53 | } 54 | 55 | [Fact] 56 | public void ConvertUppercaseStringToEnum() 57 | { 58 | var day = DayOfWeek.Thursday; 59 | var value = day.ToString().ToUpperInvariant(); 60 | var result = TypeConverter.ConvertStringToType(value, typeof(DayOfWeek)); 61 | Assert.Equal(day, result); 62 | } 63 | } -------------------------------------------------------------------------------- /src/appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2022 2 | environment: 3 | DOTNET_NOLOGO: true 4 | DOTNET_CLI_TELEMETRY_OPTOUT: true 5 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 6 | build_script: 7 | - pwsh: | 8 | Invoke-WebRequest "https://dot.net/v1/dotnet-install.ps1" -OutFile "./dotnet-install.ps1" 9 | ./dotnet-install.ps1 -JSonFile src/global.json -Architecture x64 -InstallDir 'C:\Program Files\dotnet' 10 | - dotnet build src --configuration Release 11 | - dotnet test src --configuration Release --no-build --no-restore --filter Category!=Integration 12 | test: off 13 | on_failure: 14 | - ps: Get-ChildItem *.received.* -recurse | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } 15 | artifacts: 16 | - path: nugets\*.nupkg -------------------------------------------------------------------------------- /src/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.300", 4 | "allowPrerelease": true, 5 | "rollForward": "latestFeature" 6 | } 7 | } -------------------------------------------------------------------------------- /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimonCropp/GraphQL.EntityFramework/64f441eb681b56fbc33131d57aedaf7b59d4a7d0/src/icon.png -------------------------------------------------------------------------------- /src/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimonCropp/GraphQL.EntityFramework/64f441eb681b56fbc33131d57aedaf7b59d4a7d0/src/key.snk -------------------------------------------------------------------------------- /src/mdsnippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/SimonCropp/MarkdownSnippets/master/schema.json", 3 | "TocExcludes": [ "NuGet package", "Release Notes", "Icon" ], 4 | "MaxWidth": 200, 5 | "ValidateContent": true 6 | } 7 | -------------------------------------------------------------------------------- /src/nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 20 | 21 | 24 | 25 | 26 | --------------------------------------------------------------------------------