From 061949e6b0738149e8f244711325b0a8ec159de1 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 16:53:08 +0200 Subject: [PATCH 1/2] Rebase for proper diffs style: DefaultResourceRepository and DefaultEntityService as consie 833da6a style: rename old repository interfaces 519513d style: ContextEntity --> ResourceContext d038582 style: rename Entity to Resource f2e411c style: TEntity --> TResource 3af6f9b style: DependentType, PrincipalType --> RightType, LeftType ce08b4f style: replaced principal with left and dependent with right whenev b162519 chore: remove redundant bindings throughout entire codebase e509956 refactor: regroupe hook container and executors 48495d3 style: IGenericProcessor --> IHasManyThroughUpdateHelper f19e606 chore: delete rogue file 7d235ab style: rename GenericProcessorFactory --> GenericServiceFactory. 6a186a5 chore: fix NoEntityFrameworkExample project 354a1be --- JsonApiDotnetCore.sln | 132 ++++++------- benchmarks/Query/QueryParser_Benchmarks.cs | 2 +- .../JsonApiSerializer_Benchmarks.cs | 2 +- .../Controllers/ArticlesController.cs | 1 - .../Controllers/PeopleController.cs | 1 - .../GettingStarted/Data/SampleDbContext.cs | 5 +- src/Examples/GettingStarted/Models/Person.cs | 1 - .../ModelDefinition.cs | 16 +- .../ModelsController.cs | 1 - src/Examples/GettingStarted/Startup.cs | 9 +- .../Services/CustomArticleService.cs | 6 +- .../Properties/launchSettings.json | 14 +- .../Builders/IResourceGraphBuilder.cs | 8 +- .../Builders/JsonApiApplicationBuilder.cs | 44 ++--- .../Builders/ResourceGraphBuilder.cs | 26 +-- .../Configuration/IJsonApiOptions.cs | 2 - .../Configuration/JsonApiOptions.cs | 1 - .../Controllers/BaseJsonApiController.cs | 13 +- .../Controllers/JsonApiCmdController.cs | 1 - .../Controllers/JsonApiController.cs | 7 +- .../Data/DbContextResolver.cs | 2 +- ...sitory.cs => DefaultResourceRepository.cs} | 114 +++++------ .../Data/IDbContextResolver.cs | 6 +- .../Data/IEntityRepository.cs | 18 -- .../Data/IEntityWriteRepository.cs | 24 --- ...pository.cs => IResourceReadRepository.cs} | 31 ++- .../Data/IResourceRepository.cs | 15 ++ .../Data/IResourceWriteRepository.cs | 23 +++ .../Extensions/DbContextExtensions.cs | 4 - .../Extensions/IQueryableExtensions.cs | 7 +- .../IServiceCollectionExtensions.cs | 2 - .../Extensions/TypeExtensions.cs | 2 +- .../Formatters/JsonApiReader.cs | 1 - .../Graph/ServiceDiscoveryFacade.cs | 17 +- .../Hooks/Discovery/HooksDiscovery.cs | 6 +- .../Hooks/Discovery/IHooksDiscovery.cs | 6 +- .../Hooks/Execution/EntityHashSet.cs | 8 +- .../Hooks/Execution/HookExecutorHelper.cs | 97 +++++----- .../Hooks/Execution/IHookExecutorHelper.cs | 4 +- .../Execution/RelationshipsDictionary.cs | 38 ++-- .../Hooks/IResourceHookContainer.cs | 178 ++++++++++-------- .../Hooks/IResourceHookExecutor.cs | 110 ++++++----- .../Hooks/ResourceHookExecutor.cs | 112 +++++------ .../Hooks/Traversal/ChildNode.cs | 34 ++-- .../Hooks/Traversal/IEntityNode.cs | 4 +- .../Hooks/Traversal/ITraversalHelper.cs | 4 +- .../Hooks/Traversal/RelationshipGroup.cs | 14 +- .../Hooks/Traversal/RelationshipProxy.cs | 58 +++--- .../RelationshipsFromPreviousLayer.cs | 28 +-- .../Hooks/Traversal/RootNode.cs | 27 ++- .../Hooks/Traversal/TraversalHelper.cs | 130 +++++++------ ...rovider.cs => IResourceContextProvider.cs} | 15 +- .../Contracts/IResourceGraphExplorer.cs | 3 +- .../Internal/DefaultRoutingConvention.cs | 2 - .../Exceptions/JsonApiRouteHandler.cs | 83 -------- .../Internal/Generics/GenericProcessor.cs | 108 ----------- ...sorFactory.cs => GenericServiceFactory.cs} | 24 +-- .../Generics/HasManyThroughUpdateHelper.cs | 80 ++++++++ .../Internal/IdentifiableComparer.cs | 2 - .../Internal/InverseRelationships.cs | 8 +- .../Internal/Query/FilterQuery.cs | 4 - .../{ContextEntity.cs => ResourceContext.cs} | 8 +- .../Internal/ResourceGraph.cs | 40 ++-- ...dleware.cs => CurrentRequestMiddleware.cs} | 8 +- .../Middleware/DefaultTypeMatchFilter.cs | 8 +- .../Annotation/HasManyThroughAttribute.cs | 4 +- .../Models/Annotation/HasOneAttribute.cs | 3 +- .../Annotation/RelationshipAttribute.cs | 29 +-- src/JsonApiDotNetCore/Models/IHasMeta.cs | 1 - .../Models/ResourceDefinition.cs | 10 +- .../Common/QueryParameterService.cs | 6 +- .../QueryParameterServices/FilterService.cs | 2 +- .../QueryParameterServices/IncludeService.cs | 12 +- .../QueryParameterServices/SortService.cs | 2 +- .../SparseFieldsService.cs | 12 +- .../Contracts/ICurrentRequest.cs | 8 +- .../RequestServices/CurrentRequest.cs | 10 +- .../Client/ResponseDeserializer.cs | 16 +- .../Common/BaseDocumentParser.cs | 20 +- .../Serialization/Common/DocumentBuilder.cs | 4 +- .../Common/ResourceObjectBuilder.cs | 10 +- .../Builders/IncludedResourceObjectBuilder.cs | 4 +- .../Server/Builders/LinkBuilder.cs | 34 ++-- .../Builders/ResponseResourceObjectBuilder.cs | 2 +- .../Server/Contracts/ILinkBuilder.cs | 2 +- .../Serialization/Server/FieldsToSerialize.cs | 1 - .../Server/RequestDeserializer.cs | 2 +- .../Server/ResponseSerializer.cs | 4 +- .../Server/ResponseSerializerFactory.cs | 9 +- ...ceService.cs => DefaultResourceService.cs} | 30 +-- .../Services/ResourceDefinitionProvider.cs | 2 +- .../ServiceDiscoveryFacadeTests.cs | 20 +- .../Acceptance/Spec/SparseFieldSetTests.cs | 2 +- .../Acceptance/TestFixture.cs | 1 - .../Extensions/IQueryableExtensions.cs | 8 +- .../Extensibility/NoEntityFrameworkTests.cs | 9 +- test/NoEntityFrameworkTests/TestFixture.cs | 37 +++- .../Builders/ContextGraphBuilder_Tests.cs | 24 +-- test/UnitTests/Builders/LinkBuilderTests.cs | 22 +-- .../Data/DefaultEntityRepository_Tests.cs | 8 +- test/UnitTests/DbSetMock.cs | 4 +- .../IServiceCollectionExtensionsTests.cs | 12 +- .../Internal/ContextGraphBuilder_Tests.cs | 2 +- .../QueryParameters/IncludeServiceTests.cs | 4 +- .../QueryParametersUnitTestCollection.cs | 6 +- .../SparseFieldsServiceTests.cs | 28 +-- .../AffectedEntitiesHelperTests.cs | 12 +- .../ResourceHooks/ResourceHooksTestsSetup.cs | 35 ++-- .../Common/DocumentParserTests.cs | 2 +- .../Serialization/SerializerTestsSetup.cs | 14 +- .../IncludedResourceObjectBuilderTests.cs | 4 +- .../Services/EntityResourceService_Tests.cs | 6 +- 112 files changed, 1069 insertions(+), 1239 deletions(-) rename src/JsonApiDotNetCore/Data/{DefaultEntityRepository.cs => DefaultResourceRepository.cs} (80%) delete mode 100644 src/JsonApiDotNetCore/Data/IEntityRepository.cs delete mode 100644 src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs rename src/JsonApiDotNetCore/Data/{IEntityReadRepository.cs => IResourceReadRepository.cs} (56%) create mode 100644 src/JsonApiDotNetCore/Data/IResourceRepository.cs create mode 100644 src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs rename src/JsonApiDotNetCore/Internal/Contracts/{IContextEntityProvider.cs => IResourceContextProvider.cs} (52%) delete mode 100644 src/JsonApiDotNetCore/Internal/Exceptions/JsonApiRouteHandler.cs delete mode 100644 src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs rename src/JsonApiDotNetCore/Internal/Generics/{GenericProcessorFactory.cs => GenericServiceFactory.cs} (50%) create mode 100644 src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs rename src/JsonApiDotNetCore/Internal/{ContextEntity.cs => ResourceContext.cs} (95%) rename src/JsonApiDotNetCore/Middleware/{RequestMiddleware.cs => CurrentRequestMiddleware.cs} (96%) rename src/JsonApiDotNetCore/Services/{EntityResourceService.cs => DefaultResourceService.cs} (92%) diff --git a/JsonApiDotnetCore.sln b/JsonApiDotnetCore.sln index 0b778836b4..d582f7b921 100644 --- a/JsonApiDotnetCore.sln +++ b/JsonApiDotnetCore.sln @@ -21,14 +21,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{02 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{076E1AE4-FD25-4684-B826-CAAE37FEA0AA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore", "src\JsonApiDotNetCore\JsonApiDotNetCore.csproj", "{9D36BE59-7C14-448B-984D-93A0E7816314}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCoreExample", "src\Examples\JsonApiDotNetCoreExample\JsonApiDotNetCoreExample.csproj", "{27FA7CD3-8DCD-4104-9AB4-B2D927F421B5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoEntityFrameworkExample", "src\Examples\NoEntityFrameworkExample\NoEntityFrameworkExample.csproj", "{99BAF03C-362B-41FA-9FFF-67F697EFC28C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportsExample", "src\Examples\ReportsExample\ReportsExample.csproj", "{1CC0831C-ED1D-442E-8421-331D50BD41F1}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCoreExampleTests", "test\JsonApiDotNetCoreExampleTests\JsonApiDotNetCoreExampleTests.csproj", "{CAF331F8-9255-4D72-A1A8-A54141E99F1E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoEntityFrameworkTests", "test\NoEntityFrameworkTests\NoEntityFrameworkTests.csproj", "{4F15A8F8-5BC6-45A1-BC51-03F921B726A4}" @@ -37,10 +29,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "test\UnitTests EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscoveryTests", "test\DiscoveryTests\DiscoveryTests.csproj", "{03032A2F-664D-4DD8-A82F-AD8A482EDD85}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStarted", "src\Examples\GettingStarted\GettingStarted.csproj", "{92BFF50F-BF96-43AD-AB86-A8B861C32412}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "benchmarks\Benchmarks.csproj", "{DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCoreExample", "src\Examples\JsonApiDotNetCoreExample\JsonApiDotNetCoreExample.csproj", "{C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoEntityFrameworkExample", "src\Examples\NoEntityFrameworkExample\NoEntityFrameworkExample.csproj", "{789085E1-048F-4996-B600-791B9CA3A663}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportsExample", "src\Examples\ReportsExample\ReportsExample.csproj", "{8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore", "src\JsonApiDotNetCore\JsonApiDotNetCore.csproj", "{21D27239-138D-4604-8E49-DCBE41BCE4C8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStarted", "src\Examples\GettingStarted\GettingStarted.csproj", "{067FFD7A-C66B-473D-8471-37F5C95DF61C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -51,46 +51,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9D36BE59-7C14-448B-984D-93A0E7816314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9D36BE59-7C14-448B-984D-93A0E7816314}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9D36BE59-7C14-448B-984D-93A0E7816314}.Debug|x64.ActiveCfg = Debug|Any CPU - {9D36BE59-7C14-448B-984D-93A0E7816314}.Debug|x86.ActiveCfg = Debug|Any CPU - {9D36BE59-7C14-448B-984D-93A0E7816314}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9D36BE59-7C14-448B-984D-93A0E7816314}.Release|Any CPU.Build.0 = Release|Any CPU - {9D36BE59-7C14-448B-984D-93A0E7816314}.Release|x64.ActiveCfg = Release|Any CPU - {9D36BE59-7C14-448B-984D-93A0E7816314}.Release|x86.ActiveCfg = Release|Any CPU - {27FA7CD3-8DCD-4104-9AB4-B2D927F421B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {27FA7CD3-8DCD-4104-9AB4-B2D927F421B5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {27FA7CD3-8DCD-4104-9AB4-B2D927F421B5}.Debug|x64.ActiveCfg = Debug|Any CPU - {27FA7CD3-8DCD-4104-9AB4-B2D927F421B5}.Debug|x86.ActiveCfg = Debug|Any CPU - {27FA7CD3-8DCD-4104-9AB4-B2D927F421B5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {27FA7CD3-8DCD-4104-9AB4-B2D927F421B5}.Release|Any CPU.Build.0 = Release|Any CPU - {27FA7CD3-8DCD-4104-9AB4-B2D927F421B5}.Release|x64.ActiveCfg = Release|Any CPU - {27FA7CD3-8DCD-4104-9AB4-B2D927F421B5}.Release|x86.ActiveCfg = Release|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Debug|x64.ActiveCfg = Debug|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Debug|x64.Build.0 = Debug|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Debug|x86.ActiveCfg = Debug|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Debug|x86.Build.0 = Debug|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Release|Any CPU.Build.0 = Release|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Release|x64.ActiveCfg = Release|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Release|x64.Build.0 = Release|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Release|x86.ActiveCfg = Release|Any CPU - {99BAF03C-362B-41FA-9FFF-67F697EFC28C}.Release|x86.Build.0 = Release|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Debug|x64.ActiveCfg = Debug|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Debug|x64.Build.0 = Debug|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Debug|x86.ActiveCfg = Debug|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Debug|x86.Build.0 = Debug|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Release|Any CPU.Build.0 = Release|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Release|x64.ActiveCfg = Release|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Release|x64.Build.0 = Release|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Release|x86.ActiveCfg = Release|Any CPU - {1CC0831C-ED1D-442E-8421-331D50BD41F1}.Release|x86.Build.0 = Release|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|Any CPU.Build.0 = Debug|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -135,18 +95,6 @@ Global {03032A2F-664D-4DD8-A82F-AD8A482EDD85}.Release|x64.Build.0 = Release|Any CPU {03032A2F-664D-4DD8-A82F-AD8A482EDD85}.Release|x86.ActiveCfg = Release|Any CPU {03032A2F-664D-4DD8-A82F-AD8A482EDD85}.Release|x86.Build.0 = Release|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Debug|x64.ActiveCfg = Debug|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Debug|x64.Build.0 = Debug|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Debug|x86.ActiveCfg = Debug|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Debug|x86.Build.0 = Debug|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Release|Any CPU.Build.0 = Release|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Release|x64.ActiveCfg = Release|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Release|x64.Build.0 = Release|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Release|x86.ActiveCfg = Release|Any CPU - {92BFF50F-BF96-43AD-AB86-A8B861C32412}.Release|x86.Build.0 = Release|Any CPU {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -159,21 +107,73 @@ Global {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Release|x64.Build.0 = Release|Any CPU {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Release|x86.ActiveCfg = Release|Any CPU {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Release|x86.Build.0 = Release|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|x64.ActiveCfg = Debug|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|x86.ActiveCfg = Debug|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|Any CPU.Build.0 = Release|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|x64.ActiveCfg = Release|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|x86.ActiveCfg = Release|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Debug|Any CPU.Build.0 = Debug|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Debug|x64.ActiveCfg = Debug|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Debug|x64.Build.0 = Debug|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Debug|x86.ActiveCfg = Debug|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Debug|x86.Build.0 = Debug|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Release|Any CPU.ActiveCfg = Release|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Release|Any CPU.Build.0 = Release|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Release|x64.ActiveCfg = Release|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Release|x64.Build.0 = Release|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Release|x86.ActiveCfg = Release|Any CPU + {789085E1-048F-4996-B600-791B9CA3A663}.Release|x86.Build.0 = Release|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Debug|x64.ActiveCfg = Debug|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Debug|x64.Build.0 = Debug|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Debug|x86.ActiveCfg = Debug|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Debug|x86.Build.0 = Debug|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Release|Any CPU.Build.0 = Release|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Release|x64.ActiveCfg = Release|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Release|x64.Build.0 = Release|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Release|x86.ActiveCfg = Release|Any CPU + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B}.Release|x86.Build.0 = Release|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|x64.ActiveCfg = Debug|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|x86.ActiveCfg = Debug|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|Any CPU.Build.0 = Release|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|x64.ActiveCfg = Release|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|x86.ActiveCfg = Release|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x64.ActiveCfg = Debug|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x64.Build.0 = Debug|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x86.ActiveCfg = Debug|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x86.Build.0 = Debug|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|Any CPU.Build.0 = Release|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x64.ActiveCfg = Release|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x64.Build.0 = Release|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x86.ActiveCfg = Release|Any CPU + {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {026FBC6C-AF76-4568-9B87-EC73457899FD} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF} - {9D36BE59-7C14-448B-984D-93A0E7816314} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF} - {27FA7CD3-8DCD-4104-9AB4-B2D927F421B5} = {026FBC6C-AF76-4568-9B87-EC73457899FD} - {99BAF03C-362B-41FA-9FFF-67F697EFC28C} = {026FBC6C-AF76-4568-9B87-EC73457899FD} - {1CC0831C-ED1D-442E-8421-331D50BD41F1} = {026FBC6C-AF76-4568-9B87-EC73457899FD} {CAF331F8-9255-4D72-A1A8-A54141E99F1E} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} {4F15A8F8-5BC6-45A1-BC51-03F921B726A4} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} {8788FF65-C2B6-40B2-A3A0-1E3D91C02664} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} {03032A2F-664D-4DD8-A82F-AD8A482EDD85} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C} = {076E1AE4-FD25-4684-B826-CAAE37FEA0AA} + {21D27239-138D-4604-8E49-DCBE41BCE4C8} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF} + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A} = {026FBC6C-AF76-4568-9B87-EC73457899FD} + {789085E1-048F-4996-B600-791B9CA3A663} = {026FBC6C-AF76-4568-9B87-EC73457899FD} + {8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B} = {026FBC6C-AF76-4568-9B87-EC73457899FD} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4} diff --git a/benchmarks/Query/QueryParser_Benchmarks.cs b/benchmarks/Query/QueryParser_Benchmarks.cs index 6cfe71843d..68c08e8e79 100644 --- a/benchmarks/Query/QueryParser_Benchmarks.cs +++ b/benchmarks/Query/QueryParser_Benchmarks.cs @@ -23,7 +23,7 @@ public class QueryParser_Benchmarks { public QueryParser_Benchmarks() { var requestMock = new Mock(); - requestMock.Setup(m => m.GetRequestResource()).Returns(new ContextEntity { + requestMock.Setup(m => m.GetRequestResource()).Returns(new ResourceContext { Attributes = new List { new AttrAttribute(ATTRIBUTE, ATTRIBUTE) } diff --git a/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs b/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs index b174da9cb9..19e585bdc3 100644 --- a/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs +++ b/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs @@ -34,7 +34,7 @@ // jsonApiOptions.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions); -// var genericProcessorFactoryMock = new Mock(); +// var genericServiceFactoryMock = new Mock(); // var documentBuilder = new BaseDocumentBuilder(jsonApiContextMock.Object); // _jsonApiSerializer = new JsonApiSerializer(jsonApiContextMock.Object, documentBuilder); diff --git a/src/Examples/GettingStarted/Controllers/ArticlesController.cs b/src/Examples/GettingStarted/Controllers/ArticlesController.cs index 135391e9eb..2bc928a46f 100644 --- a/src/Examples/GettingStarted/Controllers/ArticlesController.cs +++ b/src/Examples/GettingStarted/Controllers/ArticlesController.cs @@ -1,7 +1,6 @@ using GettingStarted.Models; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; namespace GettingStarted diff --git a/src/Examples/GettingStarted/Controllers/PeopleController.cs b/src/Examples/GettingStarted/Controllers/PeopleController.cs index 0725bbebaa..95eac64346 100644 --- a/src/Examples/GettingStarted/Controllers/PeopleController.cs +++ b/src/Examples/GettingStarted/Controllers/PeopleController.cs @@ -1,7 +1,6 @@ using GettingStarted.Models; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; namespace GettingStarted diff --git a/src/Examples/GettingStarted/Data/SampleDbContext.cs b/src/Examples/GettingStarted/Data/SampleDbContext.cs index 2f8fefb405..ede5e02baf 100644 --- a/src/Examples/GettingStarted/Data/SampleDbContext.cs +++ b/src/Examples/GettingStarted/Data/SampleDbContext.cs @@ -6,10 +6,7 @@ namespace GettingStarted { public class SampleDbContext : DbContext { - public SampleDbContext(DbContextOptions options) - : base(options) - { } - + public SampleDbContext(DbContextOptions options) : base(options) { } public DbSet
Articles { get; set; } public DbSet People { get; set; } public DbSet Models { get; set; } diff --git a/src/Examples/GettingStarted/Models/Person.cs b/src/Examples/GettingStarted/Models/Person.cs index 625cf26ab6..39b59a44bb 100644 --- a/src/Examples/GettingStarted/Models/Person.cs +++ b/src/Examples/GettingStarted/Models/Person.cs @@ -7,7 +7,6 @@ public class Person : Identifiable { [Attr] public string Name { get; set; } - [HasMany] public List
Articles { get; set; } } diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs index aa19ac3f71..6fd6a131f4 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs +++ b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs @@ -6,16 +6,14 @@ namespace GettingStarted.ResourceDefinitionExample { public class ModelDefinition : ResourceDefinition { - public ModelDefinition(IContextEntityProvider provider) : base(provider) + public ModelDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { + // this allows POST / PATCH requests to set the value of a + // property, but we don't include this value in the response + // this might be used if the incoming value gets hashed or + // encrypted prior to being persisted and this value should + // never be sent back to the client + HideFields(model => model.DontExpose); } - - // this allows POST / PATCH requests to set the value of a - // property, but we don't include this value in the response - // this might be used if the incoming value gets hashed or - // encrypted prior to being persisted and this value should - // never be sent back to the client - protected override List OutputAttrs() - => Remove(model => model.DontExpose); } } diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs index 6a92b4f8f3..1b488ed383 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs +++ b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; namespace GettingStarted.ResourceDefinitionExample diff --git a/src/Examples/GettingStarted/Startup.cs b/src/Examples/GettingStarted/Startup.cs index d5c805282b..f3e98948c8 100644 --- a/src/Examples/GettingStarted/Startup.cs +++ b/src/Examples/GettingStarted/Startup.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using JsonApiDotNetCore.Extensions; @@ -23,7 +18,7 @@ public void ConfigureServices(IServiceCollection services) var mvcBuilder = services.AddMvcCore(); services.AddJsonApi( options => options.Namespace = "api", - discover => discover.AddCurrentAssembly(), mvcBuilder); + discover => discover.AddCurrentAssembly(), mvcBuilder: mvcBuilder); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, SampleDbContext context) diff --git a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs b/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs index 56f923aadc..549feaaa66 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs @@ -11,16 +11,16 @@ namespace JsonApiDotNetCoreExample.Services { - public class CustomArticleService : EntityResourceService
+ public class CustomArticleService : DefaultResourceService
{ public CustomArticleService(ISortService sortService, IFilterService filterService, - IEntityRepository repository, + IResourceRepository repository, IJsonApiOptions options, IIncludeService includeService, ISparseFieldsService sparseFieldsService, IPageService pageService, - IContextEntityProvider provider, + IResourceContextProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) : base(sortService, filterService, repository, options, includeService, sparseFieldsService, diff --git a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json b/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json index 1dff6cfe69..0abc738c49 100644 --- a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json +++ b/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json @@ -8,20 +8,20 @@ } }, "profiles": { - "NoEntityFrameworkExample": { - "commandName": "Project", + "IIS Express": { + "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:5000/" + } }, - "IIS Express": { - "commandName": "IISExpress", + "NoEntityFrameworkExample": { + "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "applicationUrl": "http://localhost:5000/" } } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs index 6a3e7f2ae3..a9a74cf9a7 100644 --- a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs @@ -1,5 +1,5 @@ using System; -using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; @@ -21,7 +21,7 @@ public interface IResourceGraphBuilder /// /// The pluralized name that should be exposed by the API. /// If nothing is specified, the configured name formatter will be used. - /// See . + /// See . /// IResourceGraphBuilder AddResource(string pluralizedTypeName = null) where TResource : class, IIdentifiable; @@ -34,7 +34,7 @@ public interface IResourceGraphBuilder /// /// The pluralized name that should be exposed by the API. /// If nothing is specified, the configured name formatter will be used. - /// See . + /// See . /// IResourceGraphBuilder AddResource(string pluralizedTypeName = null) where TResource : class, IIdentifiable; @@ -46,7 +46,7 @@ public interface IResourceGraphBuilder /// /// The pluralized name that should be exposed by the API. /// If nothing is specified, the configured name formatter will be used. - /// See . + /// See . /// IResourceGraphBuilder AddResource(Type entityType, Type idType, string pluralizedTypeName = null); diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index 71928fddd4..7942e3774c 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -73,7 +73,7 @@ public void ConfigureMvc() var routingConvention = intermediateProvider.GetRequiredService(); _mvcBuilder.AddMvcOptions(opt => opt.Conventions.Insert(0, routingConvention)); - _services.AddSingleton(routingConvention); + _services.AddSingleton(routingConvention); } /// @@ -118,44 +118,44 @@ public void ConfigureServices() _services.AddSingleton(new DbContextOptionsBuilder().Options); } - _services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>)); - _services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>)); + _services.AddScoped(typeof(IResourceRepository<>), typeof(DefaultResourceRepository<>)); + _services.AddScoped(typeof(IResourceRepository<,>), typeof(DefaultResourceRepository<,>)); - _services.AddScoped(typeof(IEntityReadRepository<,>), typeof(DefaultEntityRepository<,>)); - _services.AddScoped(typeof(IEntityWriteRepository<,>), typeof(DefaultEntityRepository<,>)); + _services.AddScoped(typeof(IResourceReadRepository<,>), typeof(DefaultResourceRepository<,>)); + _services.AddScoped(typeof(IResourceWriteRepository<,>), typeof(DefaultResourceRepository<,>)); - _services.AddScoped(typeof(ICreateService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(ICreateService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(ICreateService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(ICreateService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IGetAllService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IGetAllService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IGetAllService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IGetAllService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IGetByIdService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IGetByIdService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IGetByIdService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IGetByIdService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IUpdateService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IUpdateService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IUpdateService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IUpdateService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IDeleteService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IDeleteService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IDeleteService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IDeleteService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IResourceService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IResourceService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IResourceService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IResourceService<,>), typeof(DefaultResourceService<,>)); _services.AddSingleton(JsonApiOptions); _services.AddSingleton(resourceGraph); _services.AddSingleton(); _services.AddSingleton(resourceGraph); - _services.AddSingleton(resourceGraph); + _services.AddSingleton(resourceGraph); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); - _services.AddScoped(); - _services.AddScoped(typeof(GenericProcessor<>)); + _services.AddScoped(); + _services.AddScoped(typeof(HasManyThroughUpdateHelper<>)); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index 14144d3ae8..405fe64936 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -17,7 +17,7 @@ namespace JsonApiDotNetCore.Builders { public class ResourceGraphBuilder : IResourceGraphBuilder { - private readonly List _entities = new List(); + private readonly List _entities = new List(); private readonly List _validationResults = new List(); private readonly IResourceNameFormatter _resourceNameFormatter = new KebabCaseFormatter(); @@ -36,9 +36,9 @@ public IResourceGraph Build() return resourceGraph; } - private void SetResourceLinksOptions(ContextEntity resourceContext) + private void SetResourceLinksOptions(ResourceContext resourceContext) { - var attribute = (LinksAttribute)resourceContext.EntityType.GetCustomAttribute(typeof(LinksAttribute)); + var attribute = (LinksAttribute)resourceContext.ResourceType.GetCustomAttribute(typeof(LinksAttribute)); if (attribute != null) { resourceContext.RelationshipLinks = attribute.RelationshipLinks; @@ -67,14 +67,14 @@ public IResourceGraphBuilder AddResource(Type entityType, Type idType, string pl return this; } - private ContextEntity GetEntity(string pluralizedTypeName, Type entityType, Type idType) => new ContextEntity + private ResourceContext GetEntity(string pluralizedTypeName, Type entityType, Type idType) => new ResourceContext { - EntityName = pluralizedTypeName, - EntityType = entityType, + ResourceName = pluralizedTypeName, + ResourceType = entityType, IdentityType = idType, Attributes = GetAttributes(entityType), Relationships = GetRelationships(entityType), - ResourceType = GetResourceDefinitionType(entityType) + ResourceDefinitionType = GetResourceDefinitionType(entityType) }; @@ -125,8 +125,8 @@ protected virtual List GetRelationships(Type entityType) attribute.PublicRelationshipName = attribute.PublicRelationshipName ?? _resourceNameFormatter.FormatPropertyName(prop); attribute.InternalRelationshipName = prop.Name; - attribute.DependentType = GetRelationshipType(attribute, prop); - attribute.PrincipalType = entityType; + attribute.RightType = GetRelationshipType(attribute, prop); + attribute.LeftType = entityType; attributes.Add(attribute); if (attribute is HasManyThroughAttribute hasManyThroughAttribute) @@ -160,13 +160,13 @@ protected virtual List GetRelationships(Type entityType) ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a relationship id property to type {entityType} with name {leftIdPropertyName}"); // Article → ArticleTag.Tag - hasManyThroughAttribute.RightProperty = throughProperties.SingleOrDefault(x => x.PropertyType == hasManyThroughAttribute.DependentType) - ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a navigation property to type {hasManyThroughAttribute.DependentType}"); + hasManyThroughAttribute.RightProperty = throughProperties.SingleOrDefault(x => x.PropertyType == hasManyThroughAttribute.RightType) + ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a navigation property to type {hasManyThroughAttribute.RightType}"); // ArticleTag.TagId var rightIdPropertyName = JsonApiOptions.RelatedIdMapper.GetRelatedIdPropertyName(hasManyThroughAttribute.RightProperty.Name); hasManyThroughAttribute.RightIdProperty = throughProperties.SingleOrDefault(x => x.Name == rightIdPropertyName) - ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a relationship id property to type {hasManyThroughAttribute.DependentType} with name {rightIdPropertyName}"); + ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a relationship id property to type {hasManyThroughAttribute.RightType} with name {rightIdPropertyName}"); } } @@ -234,7 +234,7 @@ private string GetResourceNameFromDbSetProperty(PropertyInfo property, Type reso private void AssertEntityIsNotAlreadyDefined(Type entityType) { - if (_entities.Any(e => e.EntityType == entityType)) + if (_entities.Any(e => e.ResourceType == entityType)) throw new InvalidOperationException($"Cannot add entity type {entityType} to context resourceGraph, there is already an entity of that type configured."); } } diff --git a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs index 661341b25a..a969a4dbf0 100644 --- a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs @@ -1,5 +1,3 @@ -using JsonApiDotNetCore.Internal.Contracts; - namespace JsonApiDotNetCore.Configuration { public interface IJsonApiOptions : ILinksConfiguration, ISerializerOptions diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index dfa0591e18..fe41af6602 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using JsonApiDotNetCore.Builders; using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Models.Links; diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs index 1b2477dc79..e0b781b283 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs @@ -4,7 +4,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Mvc; @@ -32,14 +31,11 @@ public BaseJsonApiController( IResourceService resourceService, ILoggerFactory loggerFactory) { - if(loggerFactory != null) - { + if (loggerFactory != null) _logger = loggerFactory.CreateLogger>(); - } else - { _logger = new Logger>(new LoggerFactory()); - } + _jsonApiOptions = jsonApiOptions; _getAll = resourceService; _getById = resourceService; @@ -51,7 +47,6 @@ public BaseJsonApiController( _delete = resourceService; } - public BaseJsonApiController( IJsonApiOptions jsonApiOptions, IResourceQueryService queryService = null, @@ -68,11 +63,7 @@ public BaseJsonApiController( _delete = cmdService; } - /// - /// Base constructor - /// /// - /// /// /// /// diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiCmdController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiCmdController.cs index 4887c8955d..d88ee0272d 100644 --- a/src/JsonApiDotNetCore/Controllers/JsonApiCmdController.cs +++ b/src/JsonApiDotNetCore/Controllers/JsonApiCmdController.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Threading.Tasks; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Models; diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs index 058ef5c313..6ed4ccca04 100644 --- a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; using System.Threading.Tasks; using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Mvc; @@ -11,11 +9,8 @@ namespace JsonApiDotNetCore.Controllers { public class JsonApiController : BaseJsonApiController where T : class, IIdentifiable { - /// - /// - /// + /// - /// /// /// public JsonApiController( diff --git a/src/JsonApiDotNetCore/Data/DbContextResolver.cs b/src/JsonApiDotNetCore/Data/DbContextResolver.cs index dc30159205..3c9e20b9da 100644 --- a/src/JsonApiDotNetCore/Data/DbContextResolver.cs +++ b/src/JsonApiDotNetCore/Data/DbContextResolver.cs @@ -14,7 +14,7 @@ public DbContextResolver(TContext context) public DbContext GetContext() => _context; - public DbSet GetDbSet() where TEntity : class => null; + public DbSet GetDbSet() where TResource : class => null; } } diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs similarity index 80% rename from src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs rename to src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs index 47c6bca132..117fb450e3 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs @@ -18,70 +18,70 @@ namespace JsonApiDotNetCore.Data /// Provides a default repository implementation and is responsible for /// abstracting any EF Core APIs away from the service layer. /// - public class DefaultEntityRepository : IEntityRepository - where TEntity : class, IIdentifiable + public class DefaultResourceRepository : IResourceRepository + where TResource : class, IIdentifiable { private readonly ITargetedFields _targetedFields; private readonly DbContext _context; - private readonly DbSet _dbSet; + private readonly DbSet _dbSet; private readonly IResourceGraph _resourceGraph; - private readonly IGenericProcessorFactory _genericProcessorFactory; + private readonly IGenericServiceFactory _genericServiceFactory; - public DefaultEntityRepository( + public DefaultResourceRepository( ITargetedFields targetedFields, IDbContextResolver contextResolver, IResourceGraph resourceGraph, - IGenericProcessorFactory genericProcessorFactory) - : this(targetedFields, contextResolver, resourceGraph, genericProcessorFactory, null) + IGenericServiceFactory genericServiceFactory) + : this(targetedFields, contextResolver, resourceGraph, genericServiceFactory, null) { } - public DefaultEntityRepository( + public DefaultResourceRepository( ITargetedFields targetedFields, IDbContextResolver contextResolver, IResourceGraph resourceGraph, - IGenericProcessorFactory genericProcessorFactory, + IGenericServiceFactory genericServiceFactory, ILoggerFactory loggerFactory = null) { _targetedFields = targetedFields; _resourceGraph = resourceGraph; - _genericProcessorFactory = genericProcessorFactory; + _genericServiceFactory = genericServiceFactory; _context = contextResolver.GetContext(); - _dbSet = _context.Set(); + _dbSet = _context.Set(); } /// - public virtual IQueryable Get() => _dbSet; + public virtual IQueryable Get() => _dbSet; /// - public virtual IQueryable Get(TId id) => _dbSet.Where(e => e.Id.Equals(id)); + public virtual IQueryable Get(TId id) => _dbSet.Where(e => e.Id.Equals(id)); /// - public virtual IQueryable Select(IQueryable entities, List fields) + public virtual IQueryable Select(IQueryable entities, params AttrAttribute[] fields) { - if (fields?.Count > 0) + if (fields.Any()) return entities.Select(fields); return entities; } /// - public virtual IQueryable Filter(IQueryable entities, FilterQueryContext filterQueryContext) + public virtual IQueryable Filter(IQueryable entities, FilterQueryContext filterQueryContext) { if (filterQueryContext.IsCustom) { - var query = (Func, FilterQuery, IQueryable>)filterQueryContext.CustomQuery; + var query = (Func, FilterQuery, IQueryable>)filterQueryContext.CustomQuery; return query(entities, filterQueryContext.Query); } return entities.Filter(filterQueryContext); } /// - public virtual IQueryable Sort(IQueryable entities, SortQueryContext sortQueryContext) + public virtual IQueryable Sort(IQueryable entities, SortQueryContext sortQueryContext) { return entities.Sort(sortQueryContext); } /// - public virtual async Task CreateAsync(TEntity entity) + public virtual async Task CreateAsync(TResource entity) { foreach (var relationshipAttr in _targetedFields.Relationships) { @@ -150,7 +150,7 @@ private bool IsHasOneRelationship(string internalRelationshipName, Type type) return !type.GetProperty(internalRelationshipName).PropertyType.Inherits(typeof(IEnumerable)); } - private void DetachRelationships(TEntity entity) + private void DetachRelationships(TResource entity) { foreach (var relationshipAttr in _targetedFields.Relationships) { @@ -176,7 +176,7 @@ private void DetachRelationships(TEntity entity) } /// - public virtual async Task UpdateAsync(TEntity updatedEntity) + public virtual async Task UpdateAsync(TResource updatedEntity) { var databaseEntity = await Get(updatedEntity.Id).FirstOrDefaultAsync(); if (databaseEntity == null) @@ -212,7 +212,7 @@ public virtual async Task UpdateAsync(TEntity updatedEntity) /// to the change tracker. It does so by checking if there already are /// instances of the to-be-attached entities in the change tracker. /// - private object GetTrackedRelationshipValue(RelationshipAttribute relationshipAttr, TEntity entity, out bool wasAlreadyAttached) + private object GetTrackedRelationshipValue(RelationshipAttribute relationshipAttr, TResource entity, out bool wasAlreadyAttached) { wasAlreadyAttached = false; if (relationshipAttr is HasOneAttribute hasOneAttr) @@ -239,8 +239,8 @@ private IList GetTrackedManyRelationshipValue(IEnumerable relatio { // convert each element in the value list to relationshipAttr.DependentType. var tracked = AttachOrGetTracked(pointer); if (tracked != null) _wasAlreadyAttached = true; - return Convert.ChangeType(tracked ?? pointer, relationshipAttr.DependentType); - }).ToList().Cast(relationshipAttr.DependentType); + return Convert.ChangeType(tracked ?? pointer, relationshipAttr.RightType); + }).ToList().Cast(relationshipAttr.RightType); if (_wasAlreadyAttached) wasAlreadyAttached = true; return (IList)trackedPointerCollection; } @@ -256,16 +256,20 @@ private IIdentifiable GetTrackedHasOneRelationshipValue(IIdentifiable relationsh /// public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds) { - // TODO: it would be better to let this be determined within the relationship attribute... - // need to think about the right way to do that since HasMany doesn't need to think about this - // and setting the HasManyThrough.Type to the join type (ArticleTag instead of Tag) for this changes the semantics - // of the property... - var typeToUpdate = (relationship is HasManyThroughAttribute hasManyThrough) - ? hasManyThrough.ThroughType - : relationship.DependentType; - - var genericProcessor = _genericProcessorFactory.GetProcessor(typeof(GenericProcessor<>), typeToUpdate); - await genericProcessor.UpdateRelationshipsAsync(parent, relationship, relationshipIds); + if (relationship is HasManyThroughAttribute hasManyThrough) + { + var helper = _genericServiceFactory.Get(typeof(HasManyThroughUpdateHelper<>), hasManyThrough.ThroughType); + await helper.UpdateAsync((IIdentifiable)parent, hasManyThrough, relationshipIds); + return; + } + + var context = _context.Set(relationship.RightType); + var updatedValue = relationship is HasManyAttribute + ? context.Where(e => relationshipIds.Contains(((IIdentifiable)e).StringId)).Cast(relationship.RightType) + : context.FirstOrDefault(e => relationshipIds.First() == ((IIdentifiable)e).StringId); + + relationship.SetValue(parent, updatedValue); + await _context.SaveChangesAsync(); } /// @@ -278,7 +282,7 @@ public virtual async Task DeleteAsync(TId id) return true; } - public virtual IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain) + public virtual IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain) { if (!inclusionChain.Any()) return entities; @@ -293,7 +297,7 @@ public virtual IQueryable Include(IQueryable entities, params } /// - public virtual async Task> PageAsync(IQueryable entities, int pageSize, int pageNumber) + public virtual async Task> PageAsync(IQueryable entities, int pageSize, int pageNumber) { if (pageNumber >= 0) { @@ -315,25 +319,25 @@ public virtual async Task> PageAsync(IQueryable en } /// - public async Task CountAsync(IQueryable entities) + public async Task CountAsync(IQueryable entities) { - return (entities is IAsyncEnumerable) + return (entities is IAsyncEnumerable) ? await entities.CountAsync() : entities.Count(); } /// - public async Task FirstOrDefaultAsync(IQueryable entities) + public async Task FirstOrDefaultAsync(IQueryable entities) { - return (entities is IAsyncEnumerable) + return (entities is IAsyncEnumerable) ? await entities.FirstOrDefaultAsync() : entities.FirstOrDefault(); } /// - public async Task> ToListAsync(IQueryable entities) + public async Task> ToListAsync(IQueryable entities) { - return (entities is IAsyncEnumerable) + return (entities is IAsyncEnumerable) ? await entities.ToListAsync() : entities.ToList(); } @@ -351,7 +355,7 @@ public async Task> ToListAsync(IQueryable entiti /// after which the reassignment `p1.todoItems = [t3, t4]` will actually /// make EF Core perform a complete replace. This method does the loading of `[t1, t2]`. /// - protected void LoadCurrentRelationships(TEntity oldEntity, RelationshipAttribute relationshipAttribute) + protected void LoadCurrentRelationships(TResource oldEntity, RelationshipAttribute relationshipAttribute) { if (relationshipAttribute is HasManyThroughAttribute throughAttribute) { @@ -365,11 +369,11 @@ protected void LoadCurrentRelationships(TEntity oldEntity, RelationshipAttribute /// /// The relationshipValue parameter contains the dependent side of the relationship (Tags). - /// We can't directly add them to the principal entity (Article): we need to + /// We can't directly add them to the left entity (Article): we need to /// use the join table (ArticleTags). This methods assigns the relationship value to entity /// by taking care of that /// - private void AssignHasManyThrough(TEntity entity, HasManyThroughAttribute hasManyThrough, IList relationshipValue) + private void AssignHasManyThrough(TResource entity, HasManyThroughAttribute hasManyThrough, IList relationshipValue) { var pointers = relationshipValue.Cast(); var throughRelationshipCollection = Activator.CreateInstance(hasManyThrough.ThroughProperty.PropertyType) as IList; @@ -414,23 +418,23 @@ private IIdentifiable AttachOrGetTracked(IIdentifiable relationshipValue) } /// - public class DefaultEntityRepository : DefaultEntityRepository, IEntityRepository - where TEntity : class, IIdentifiable + public class DefaultResourceRepository : DefaultResourceRepository, IResourceRepository + where TResource : class, IIdentifiable { - public DefaultEntityRepository(ITargetedFields targetedFields, + public DefaultResourceRepository(ITargetedFields targetedFields, IDbContextResolver contextResolver, - IResourceGraph contextEntityProvider, - IGenericProcessorFactory genericProcessorFactory) - : base(targetedFields, contextResolver, contextEntityProvider, genericProcessorFactory) + IResourceGraph resourceContextProvider, + IGenericServiceFactory genericServiceFactory) + : base(targetedFields, contextResolver, resourceContextProvider, genericServiceFactory) { } - public DefaultEntityRepository(ITargetedFields targetedFields, + public DefaultResourceRepository(ITargetedFields targetedFields, IDbContextResolver contextResolver, - IResourceGraph contextEntityProvider, - IGenericProcessorFactory genericProcessorFactory, + IResourceGraph resourceContextProvider, + IGenericServiceFactory genericServiceFactory, ILoggerFactory loggerFactory = null) - : base(targetedFields, contextResolver, contextEntityProvider, genericProcessorFactory, loggerFactory) + : base(targetedFields, contextResolver, resourceContextProvider, genericServiceFactory, loggerFactory) { } } diff --git a/src/JsonApiDotNetCore/Data/IDbContextResolver.cs b/src/JsonApiDotNetCore/Data/IDbContextResolver.cs index 4915d788c4..cb5372c29d 100644 --- a/src/JsonApiDotNetCore/Data/IDbContextResolver.cs +++ b/src/JsonApiDotNetCore/Data/IDbContextResolver.cs @@ -7,8 +7,8 @@ public interface IDbContextResolver { DbContext GetContext(); - [Obsolete("Use DbContext.Set() instead", error: true)] - DbSet GetDbSet() - where TEntity : class; + [Obsolete("Use DbContext.Set() instead", error: true)] + DbSet GetDbSet() + where TResource : class; } } diff --git a/src/JsonApiDotNetCore/Data/IEntityRepository.cs b/src/JsonApiDotNetCore/Data/IEntityRepository.cs deleted file mode 100644 index 2c65b0a76a..0000000000 --- a/src/JsonApiDotNetCore/Data/IEntityRepository.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Data -{ - public interface IEntityRepository - : IEntityRepository - where TEntity : class, IIdentifiable - { } - - public interface IEntityRepository - : IEntityReadRepository, - IEntityWriteRepository - where TEntity : class, IIdentifiable - { } -} - - diff --git a/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs b/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs deleted file mode 100644 index 60c632f6a3..0000000000 --- a/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Data -{ - public interface IEntityWriteRepository - : IEntityWriteRepository - where TEntity : class, IIdentifiable - { } - - public interface IEntityWriteRepository - where TEntity : class, IIdentifiable - { - Task CreateAsync(TEntity entity); - - Task UpdateAsync(TEntity entity); - - Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds); - - Task DeleteAsync(TId id); - } -} diff --git a/src/JsonApiDotNetCore/Data/IEntityReadRepository.cs b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs similarity index 56% rename from src/JsonApiDotNetCore/Data/IEntityReadRepository.cs rename to src/JsonApiDotNetCore/Data/IResourceReadRepository.cs index 605a07257d..6480131c7e 100644 --- a/src/JsonApiDotNetCore/Data/IEntityReadRepository.cs +++ b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -7,27 +6,27 @@ namespace JsonApiDotNetCore.Data { - public interface IEntityReadRepository - : IEntityReadRepository - where TEntity : class, IIdentifiable + public interface IResourceReadRepository + : IResourceReadRepository + where TResource : class, IIdentifiable { } - public interface IEntityReadRepository - where TEntity : class, IIdentifiable + public interface IResourceReadRepository + where TResource : class, IIdentifiable { /// /// The base GET query. This is a good place to apply rules that should affect all reads, /// such as authorization of resources. /// - IQueryable Get(); + IQueryable Get(); /// /// Get the entity by id /// - IQueryable Get(TId id); + IQueryable Get(TId id); /// /// Apply fields to the provided queryable /// - IQueryable Select(IQueryable entities, List fields); + IQueryable Select(IQueryable entities, params AttrAttribute[] fields); /// /// Include a relationship in the query /// @@ -36,30 +35,30 @@ public interface IEntityReadRepository /// _todoItemsRepository.GetAndIncludeAsync(1, "achieved-date"); /// /// - IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain); + IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain); /// /// Apply a filter to the provided queryable /// - IQueryable Filter(IQueryable entities, FilterQueryContext filterQuery); + IQueryable Filter(IQueryable entities, FilterQueryContext filterQuery); /// /// Apply a sort to the provided queryable /// - IQueryable Sort(IQueryable entities, SortQueryContext sortQueries); + IQueryable Sort(IQueryable entities, SortQueryContext sortQueries); /// /// Paginate the provided queryable /// - Task> PageAsync(IQueryable entities, int pageSize, int pageNumber); + Task> PageAsync(IQueryable entities, int pageSize, int pageNumber); /// /// Count the total number of records /// - Task CountAsync(IQueryable entities); + Task CountAsync(IQueryable entities); /// /// Get the first element in the collection, return the default value if collection is empty /// - Task FirstOrDefaultAsync(IQueryable entities); + Task FirstOrDefaultAsync(IQueryable entities); /// /// Convert the collection to a materialized list /// - Task> ToListAsync(IQueryable entities); + Task> ToListAsync(IQueryable entities); } } diff --git a/src/JsonApiDotNetCore/Data/IResourceRepository.cs b/src/JsonApiDotNetCore/Data/IResourceRepository.cs new file mode 100644 index 0000000000..100ea63961 --- /dev/null +++ b/src/JsonApiDotNetCore/Data/IResourceRepository.cs @@ -0,0 +1,15 @@ +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Data +{ + public interface IResourceRepository + : IResourceRepository + where TResource : class, IIdentifiable + { } + + public interface IResourceRepository + : IResourceReadRepository, + IResourceWriteRepository + where TResource : class, IIdentifiable + { } +} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs b/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs new file mode 100644 index 0000000000..ea70d70fa8 --- /dev/null +++ b/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Data +{ + public interface IResourceWriteRepository + : IResourceWriteRepository + where TResource : class, IIdentifiable + { } + + public interface IResourceWriteRepository + where TResource : class, IIdentifiable + { + Task CreateAsync(TResource entity); + + Task UpdateAsync(TResource entity); + + Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds); + + Task DeleteAsync(TId id); + } +} diff --git a/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs b/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs index 984b396243..ebe89815f8 100644 --- a/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs @@ -10,10 +10,6 @@ namespace JsonApiDotNetCore.Extensions { public static class DbContextExtensions { - [Obsolete("This is no longer required since the introduction of context.Set", error: false)] - public static DbSet GetDbSet(this DbContext context) where T : class - => context.Set(); - /// /// Get the DbSet when the model type is unknown until runtime /// diff --git a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs index 387243fbaa..bb00eef3f3 100644 --- a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs @@ -5,7 +5,6 @@ using System.Linq.Expressions; using System.Reflection; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Internal.Query; using JsonApiDotNetCore.Models; @@ -65,7 +64,7 @@ public static IQueryable Filter(this IQueryable sourc return CallGenericWhereMethod(source, filterQuery); } - public static IQueryable Select(this IQueryable source, List columns) + public static IQueryable Select(this IQueryable source, AttrAttribute[] columns) => CallGenericSelectMethod(source, columns.Select(attr => attr.InternalAttributeName).ToList()); public static IOrderedQueryable Sort(this IQueryable source, SortQueryContext sortQuery) @@ -124,7 +123,7 @@ private static IOrderedQueryable CallGenericOrderMethod(IQuery return (IOrderedQueryable)result; } - private static Expression GetFilterExpressionLambda(Expression left, Expression right, FilterOperation operation) + private static Expression GetFilterExpressionLambda(Expression left, Expression right, FilterOperation operation) { Expression body; switch (operation) @@ -259,7 +258,7 @@ private static IQueryable CallGenericWhereMethod(IQueryable(this IList list, IEnumerable items) /// /// Extension to use the LINQ cast method in a non-generic way: /// - /// Type targetType = typeof(TEntity) + /// Type targetType = typeof(TResource) /// ((IList)myList).Cast(targetType). /// /// diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs index c51af2c5b6..d00f47526e 100644 --- a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs +++ b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading.Tasks; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Managers.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Serialization.Server; using Microsoft.AspNetCore.Mvc.Formatters; diff --git a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs index d197d55ecc..074914faa3 100644 --- a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs +++ b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs @@ -1,11 +1,8 @@ using JsonApiDotNetCore.Builders; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; -using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System; @@ -41,12 +38,12 @@ public class ServiceDiscoveryFacade : IServiceDiscoveryFacade }; internal static HashSet RepositoryInterfaces = new HashSet { - typeof(IEntityRepository<>), - typeof(IEntityRepository<,>), - typeof(IEntityWriteRepository<>), - typeof(IEntityWriteRepository<,>), - typeof(IEntityReadRepository<>), - typeof(IEntityReadRepository<,>) + typeof(IResourceRepository<>), + typeof(IResourceRepository<,>), + typeof(IResourceWriteRepository<>), + typeof(IResourceWriteRepository<,>), + typeof(IResourceReadRepository<>), + typeof(IResourceReadRepository<,>) }; private readonly IServiceCollection _services; private readonly IResourceGraphBuilder _resourceGraphBuilder; @@ -173,7 +170,7 @@ private void AddServices(Assembly assembly, ResourceDescriptor resourceDescripto } /// - /// Add implementations to container. + /// Add implementations to container. /// /// The assembly to search for resources in. public ServiceDiscoveryFacade AddRepositories(Assembly assembly) diff --git a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs index 3d5490e42f..9b95e24562 100644 --- a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs +++ b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCore.Hooks /// /// The default implementation for IHooksDiscovery /// - public class HooksDiscovery : IHooksDiscovery where TEntity : class, IIdentifiable + public class HooksDiscovery : IHooksDiscovery where TResource : class, IIdentifiable { private readonly ResourceHook[] _allHooks; private readonly ResourceHook[] _databaseValuesAttributeAllowed = @@ -40,8 +40,8 @@ public HooksDiscovery() /// The implemented hooks for model. void DiscoverImplementedHooksForModel() { - Type parameterizedResourceDefinition = typeof(ResourceDefinition); - var derivedTypes = TypeLocator.GetDerivedTypes(typeof(TEntity).Assembly, parameterizedResourceDefinition).ToList(); + Type parameterizedResourceDefinition = typeof(ResourceDefinition); + var derivedTypes = TypeLocator.GetDerivedTypes(typeof(TResource).Assembly, parameterizedResourceDefinition).ToList(); var implementedHooks = new List(); diff --git a/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs b/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs index 709b30900e..9db0d2a0ca 100644 --- a/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs +++ b/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs @@ -4,11 +4,11 @@ namespace JsonApiDotNetCore.Hooks { /// - /// A singleton service for a particular TEntity that stores a field of + /// A singleton service for a particular TResource that stores a field of /// enums that represents which resource hooks have been implemented for that /// particular entity. /// - public interface IHooksDiscovery : IHooksDiscovery where TEntity : class, IIdentifiable + public interface IHooksDiscovery : IHooksDiscovery where TResource : class, IIdentifiable { } @@ -17,7 +17,7 @@ public interface IHooksDiscovery : IHooksDiscovery where TEntity : clas public interface IHooksDiscovery { /// - /// A list of the implemented hooks for resource TEntity + /// A list of the implemented hooks for resource TResource /// /// The implemented hooks. ResourceHook[] ImplementedHooks { get; } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs b/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs index 25df58b79b..93ee588a7a 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs @@ -44,15 +44,15 @@ internal EntityHashSet(IEnumerable entities, /// - public Dictionary> GetByRelationship(Type principalType) + public Dictionary> GetByRelationship(Type leftType) { - return _relationships.GetByRelationship(principalType); + return _relationships.GetByRelationship(leftType); } /// - public Dictionary> GetByRelationship() where TRelatedResource : class, IIdentifiable + public Dictionary> GetByRelationship() where TRightResource : class, IIdentifiable { - return GetByRelationship(typeof(TRelatedResource)); + return GetByRelationship(typeof(TRightResource)); } /// diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index b92619b691..5e2ee7731d 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -7,11 +7,9 @@ using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Extensions; -using PrincipalType = System.Type; -using DependentType = System.Type; -using Microsoft.EntityFrameworkCore; +using LeftType = System.Type; +using RightType = System.Type; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Configuration; namespace JsonApiDotNetCore.Hooks @@ -21,32 +19,32 @@ internal class HookExecutorHelper : IHookExecutorHelper { private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); private readonly IJsonApiOptions _options; - protected readonly IGenericProcessorFactory _genericProcessorFactory; - protected readonly Dictionary _hookContainers; - protected readonly Dictionary _hookDiscoveries; + protected readonly IGenericServiceFactory _genericProcessorFactory; + protected readonly Dictionary _hookContainers; + protected readonly Dictionary _hookDiscoveries; protected readonly List _targetedHooksForRelatedEntities; - public HookExecutorHelper(IGenericProcessorFactory genericProcessorFactory, + public HookExecutorHelper(IGenericServiceFactory genericProcessorFactory, IJsonApiOptions options) { _options = options; _genericProcessorFactory = genericProcessorFactory; - _hookContainers = new Dictionary(); - _hookDiscoveries = new Dictionary(); + _hookContainers = new Dictionary(); + _hookDiscoveries = new Dictionary(); _targetedHooksForRelatedEntities = new List(); } /// - public IResourceHookContainer GetResourceHookContainer(DependentType dependentType, ResourceHook hook = ResourceHook.None) + public IResourceHookContainer GetResourceHookContainer(RightType rightType, ResourceHook hook = ResourceHook.None) { /// checking the cache if we have a reference for the requested container, /// regardless of the hook we will use it for. If the value is null, /// it means there was no implementation IResourceHookContainer at all, /// so we need not even bother. - if (!_hookContainers.TryGetValue(dependentType, out IResourceHookContainer container)) + if (!_hookContainers.TryGetValue(rightType, out IResourceHookContainer container)) { - container = (_genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), dependentType)); - _hookContainers[dependentType] = container; + container = (_genericProcessorFactory.Get(typeof(ResourceDefinition<>), rightType)); + _hookContainers[rightType] = container; } if (container == null) return container; @@ -65,18 +63,18 @@ public IResourceHookContainer GetResourceHookContainer(DependentType dependentTy foreach (ResourceHook targetHook in targetHooks) { - if (ShouldExecuteHook(dependentType, targetHook)) return container; + if (ShouldExecuteHook(rightType, targetHook)) return container; } return null; } /// - public IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable + public IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TResource : class, IIdentifiable { - return (IResourceHookContainer)GetResourceHookContainer(typeof(TEntity), hook); + return (IResourceHookContainer)GetResourceHookContainer(typeof(TResource), hook); } - public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] inclusionChain) + public IEnumerable LoadDbValues(LeftType entityTypeForRepository, IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] inclusionChain) { var idType = TypeHelper.GetIdentifierType(entityTypeForRepository); var parameterizedGetWhere = GetType() @@ -89,34 +87,25 @@ public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerab return (IEnumerable)Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(entityTypeForRepository), values.Cast(entityTypeForRepository)); } - public HashSet LoadDbValues(IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] relationships) where TEntity : class, IIdentifiable + public HashSet LoadDbValues(IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] relationships) where TResource : class, IIdentifiable { - var entityType = typeof(TEntity); - var dbValues = LoadDbValues(entityType, entities, hook, relationships)?.Cast(); + var entityType = typeof(TResource); + var dbValues = LoadDbValues(entityType, entities, hook, relationships)?.Cast(); if (dbValues == null) return null; - return new HashSet(dbValues); + return new HashSet(dbValues); } - public bool ShouldLoadDbValues(Type entityType, ResourceHook hook) { var discovery = GetHookDiscovery(entityType); - if (discovery.DatabaseValuesDisabledHooks.Contains(hook)) - { return false; - } if (discovery.DatabaseValuesEnabledHooks.Contains(hook)) - { return true; - } - else - { - return _options.LoaDatabaseValues; - } + return _options.LoaDatabaseValues; } - bool ShouldExecuteHook(DependentType entityType, ResourceHook hook) + bool ShouldExecuteHook(RightType entityType, ResourceHook hook) { var discovery = GetHookDiscovery(entityType); return discovery.ImplementedHooks.Contains(hook); @@ -134,66 +123,66 @@ IHooksDiscovery GetHookDiscovery(Type entityType) { if (!_hookDiscoveries.TryGetValue(entityType, out IHooksDiscovery discovery)) { - discovery = _genericProcessorFactory.GetProcessor(typeof(IHooksDiscovery<>), entityType); + discovery = _genericProcessorFactory.Get(typeof(IHooksDiscovery<>), entityType); _hookDiscoveries[entityType] = discovery; } return discovery; } - IEnumerable GetWhereAndInclude(IEnumerable ids, RelationshipAttribute[] inclusionChain) where TEntity : class, IIdentifiable + IEnumerable GetWhereAndInclude(IEnumerable ids, RelationshipAttribute[] inclusionChain) where TResource : class, IIdentifiable { - var repo = GetRepository(); + var repo = GetRepository(); var query = repo.Get().Where(e => ids.Contains(e.Id)); return repo.Include(query, inclusionChain).ToList(); } - IEntityReadRepository GetRepository() where TEntity : class, IIdentifiable + IResourceReadRepository GetRepository() where TResource : class, IIdentifiable { - return _genericProcessorFactory.GetProcessor>(typeof(IEntityReadRepository<,>), typeof(TEntity), typeof(TId)); + return _genericProcessorFactory.Get>(typeof(IResourceReadRepository<,>), typeof(TResource), typeof(TId)); } public Dictionary LoadImplicitlyAffected( - Dictionary principalEntitiesByRelation, - IEnumerable existingDependentEntities = null) + Dictionary leftEntitiesByRelation, + IEnumerable existingRightEntities = null) { var implicitlyAffected = new Dictionary(); - foreach (var kvp in principalEntitiesByRelation) + foreach (var kvp in leftEntitiesByRelation) { - if (IsHasManyThrough(kvp, out var principals, out var relationship)) continue; + if (IsHasManyThrough(kvp, out var lefts, out var relationship)) continue; // note that we dont't have to check if BeforeImplicitUpdate hook is implemented. If not, it wont ever get here. - var includedPrincipals = LoadDbValues(relationship.PrincipalType, principals, ResourceHook.BeforeImplicitUpdateRelationship, relationship); + var includedLefts = LoadDbValues(relationship.LeftType, lefts, ResourceHook.BeforeImplicitUpdateRelationship, relationship); - foreach (IIdentifiable ip in includedPrincipals) + foreach (IIdentifiable ip in includedLefts) { - IList dbDependentEntityList = null; + IList dbRightEntityList = null; var relationshipValue = relationship.GetValue(ip); if (!(relationshipValue is IEnumerable)) { - dbDependentEntityList = TypeHelper.CreateListFor(relationship.DependentType); - if (relationshipValue != null) dbDependentEntityList.Add(relationshipValue); + dbRightEntityList = TypeHelper.CreateListFor(relationship.RightType); + if (relationshipValue != null) dbRightEntityList.Add(relationshipValue); } else { - dbDependentEntityList = (IList)relationshipValue; + dbRightEntityList = (IList)relationshipValue; } - var dbDependentEntityListCasted = dbDependentEntityList.Cast().ToList(); - if (existingDependentEntities != null) dbDependentEntityListCasted = dbDependentEntityListCasted.Except(existingDependentEntities.Cast(), _comparer).ToList(); + var dbRightEntityListCasted = dbRightEntityList.Cast().ToList(); + if (existingRightEntities != null) dbRightEntityListCasted = dbRightEntityListCasted.Except(existingRightEntities.Cast(), _comparer).ToList(); - if (dbDependentEntityListCasted.Any()) + if (dbRightEntityListCasted.Any()) { if (!implicitlyAffected.TryGetValue(relationship, out IEnumerable affected)) { - affected = TypeHelper.CreateListFor(relationship.DependentType); + affected = TypeHelper.CreateListFor(relationship.RightType); implicitlyAffected[relationship] = affected; } - ((IList)affected).AddRange(dbDependentEntityListCasted); + ((IList)affected).AddRange(dbRightEntityListCasted); } } } - return implicitlyAffected.ToDictionary(kvp => kvp.Key, kvp => TypeHelper.CreateHashSetFor(kvp.Key.DependentType, kvp.Value)); + return implicitlyAffected.ToDictionary(kvp => kvp.Key, kvp => TypeHelper.CreateHashSetFor(kvp.Key.RightType, kvp.Value)); } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs index 98de544f0a..214f1b000d 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs @@ -31,13 +31,13 @@ internal interface IHookExecutorHelper /// Also caches the retrieves containers so we don't need to reflectively /// instantiate them multiple times. /// - IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable; + IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TResource : class, IIdentifiable; /// /// Load the implicitly affected entities from the database for a given set of target target entities and involved relationships /// /// The implicitly affected entities by relationship - Dictionary LoadImplicitlyAffected(Dictionary principalEntities, IEnumerable existingDependentEntities = null); + Dictionary LoadImplicitlyAffected(Dictionary leftEntities, IEnumerable existingRightEntities = null); /// /// For a set of entities, loads current values from the database diff --git a/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs b/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs index b2f1a8fbec..45b6cd0eca 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs @@ -16,51 +16,51 @@ public interface IRelationshipsDictionary { } /// /// An interface that is implemented to expose a relationship dictionary on another class. /// - public interface IByAffectedRelationships : - IRelationshipGetters where TDependentResource : class, IIdentifiable + public interface IByAffectedRelationships : + IRelationshipGetters where TRightResource : class, IIdentifiable { /// /// Gets a dictionary of affected resources grouped by affected relationships. /// - Dictionary> AffectedRelationships { get; } + Dictionary> AffectedRelationships { get; } } /// /// A helper class that provides insights in which relationships have been updated for which entities. /// - public interface IRelationshipsDictionary : - IRelationshipGetters, - IReadOnlyDictionary>, - IRelationshipsDictionary where TDependentResource : class, IIdentifiable + public interface IRelationshipsDictionary : + IRelationshipGetters, + IReadOnlyDictionary>, + IRelationshipsDictionary where TRightResource : class, IIdentifiable { } /// /// A helper class that provides insights in which relationships have been updated for which entities. /// - public interface IRelationshipGetters where TResource : class, IIdentifiable + public interface IRelationshipGetters where TLeftResource : class, IIdentifiable { /// - /// Gets a dictionary of all entities that have an affected relationship to type + /// Gets a dictionary of all entities that have an affected relationship to type /// - Dictionary> GetByRelationship() where TRelatedResource : class, IIdentifiable; + Dictionary> GetByRelationship() where TRightResource : class, IIdentifiable; /// - /// Gets a dictionary of all entities that have an affected relationship to type + /// Gets a dictionary of all entities that have an affected relationship to type /// - Dictionary> GetByRelationship(Type relatedResourceType); + Dictionary> GetByRelationship(Type relatedResourceType); /// - /// Gets a collection of all the entities for the property within + /// Gets a collection of all the entities for the property within /// has been affected by the request /// - /// - HashSet GetAffected(Expression> NavigationAction); + /// + HashSet GetAffected(Expression> navigationAction); } /// - /// Implementation of IAffectedRelationships{TDependentResource} + /// Implementation of IAffectedRelationships{TRightResource} /// - /// It is practically a ReadOnlyDictionary{RelationshipAttribute, HashSet{TDependentResource}} dictionary - /// with the two helper methods defined on IAffectedRelationships{TDependentResource}. + /// It is practically a ReadOnlyDictionary{RelationshipAttribute, HashSet{TRightResource}} dictionary + /// with the two helper methods defined on IAffectedRelationships{TRightResource}. /// public class RelationshipsDictionary : Dictionary>, @@ -87,7 +87,7 @@ public Dictionary> GetByRelationship public Dictionary> GetByRelationship(Type relatedType) { - return this.Where(p => p.Key.DependentType == relatedType).ToDictionary(p => p.Key, p => p.Value); + return this.Where(p => p.Key.RightType == relatedType).ToDictionary(p => p.Key, p => p.Value); } /// diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs index f0f45ab276..cffc1371c9 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs @@ -12,21 +12,48 @@ public interface IResourceHookContainer { } /// /// Implement this interface to implement business logic hooks on . /// - public interface IResourceHookContainer : IBeforeHooks, IAfterHooks, IOnHooks, IResourceHookContainer where TResource : class, IIdentifiable { } + public interface IResourceHookContainer + : IReadHookContainer, IDeleteHookContainer, ICreateHookContainer, + IUpdateHookContainer, IOnReturnHookContainer, IResourceHookContainer + where TResource : class, IIdentifiable { } /// - /// Wrapper interface for all Before hooks. + /// Read hooks container /// - public interface IBeforeHooks where TResource : class, IIdentifiable + public interface IReadHookContainer where TResource : class, IIdentifiable { /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the + /// layer just before reading entities of type . + /// + /// An enum indicating from where the hook was triggered. + /// Indicates whether the to be queried entities are the main request entities or if they were included + /// The string id of the requested entity, in the case of + void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null); + /// + /// Implement this hook to run custom logic in the + /// layer just after reading entities of type . + /// + /// The unique set of affected entities. + /// An enum indicating from where the hook was triggered. + /// A boolean to indicate whether the entities in this hook execution are the main entities of the request, + /// or if they were included as a relationship + void AfterRead(HashSet entities, ResourcePipeline pipeline, bool isIncluded = false); + } + + /// + /// Create hooks container + /// + public interface ICreateHookContainer where TResource : class, IIdentifiable + { + /// + /// Implement this hook to run custom logic in the /// layer just before creation of entities of type . /// /// For the pipeline, /// will typically contain one entry. /// - /// The returned may be a subset + /// The returned may be a subset /// of , in which case the operation of the /// pipeline will not be executed for the omitted entities. The returned /// set may also contain custom changes of the properties on the entities. @@ -40,23 +67,36 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. IEnumerable BeforeCreate(IEntityHashSet entities, ResourcePipeline pipeline); + /// - /// Implement this hook to run custom logic in the - /// layer just before reading entities of type . + /// Implement this hook to run custom logic in the + /// layer just after creation of entities of type . + /// + /// If relationships were created with the created entities, this will + /// be reflected by the corresponding NavigationProperty being set. + /// For each of these relationships, the + /// hook is fired after the execution of this hook. /// + /// The transformed entity set + /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. - /// Indicates whether the to be queried entities are the main request entities or if they were included - /// The string id of the requested entity, in the case of - void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null); + void AfterCreate(HashSet entities, ResourcePipeline pipeline); + } + + /// + /// update hooks container + /// + public interface IUpdateHookContainer where TResource : class, IIdentifiable + { /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just before updating entities of type . /// /// For the pipeline, the /// will typically contain one entity. /// - /// The returned may be a subset - /// of the property in parameter , + /// The returned may be a subset + /// of the property in parameter , /// in which case the operation of the pipeline will not be executed /// for the omitted entities. The returned set may also contain custom /// changes of the properties on the entities. @@ -77,27 +117,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable IEnumerable BeforeUpdate(IDiffableEntityHashSet entities, ResourcePipeline pipeline); /// - /// Implement this hook to run custom logic in the - /// layer just before deleting entities of type . - /// - /// For the pipeline, - /// will typically contain one entity. - /// - /// The returned may be a subset - /// of , in which case the operation of the - /// pipeline will not be executed for the omitted entities. - /// - /// If by the deletion of these entities any other entities are affected - /// implicitly by the removal of their relationships (eg - /// in the case of an one-to-one relationship), the - /// hook is fired for these entities. - /// - /// The transformed entity set - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - IEnumerable BeforeDelete(IEntityHashSet entities, ResourcePipeline pipeline); - /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just before updating relationships to entities of type . /// /// This hook is fired when a relationship is created to entities of type @@ -106,7 +126,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// and its author relationship was set to an existing Person, this hook will be fired /// for that particular Person. /// - /// The returned may be a subset + /// The returned may be a subset /// of , in which case the operation of the /// pipeline will not be executed for any entity whose id was omitted /// @@ -116,8 +136,30 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// An enum indicating from where the hook was triggered. /// A helper that groups the entities by the affected relationship IEnumerable BeforeUpdateRelationship(HashSet ids, IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); + /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the + /// layer just after updating entities of type . + /// + /// If relationships were updated with the updated entities, this will + /// be reflected by the corresponding NavigationProperty being set. + /// For each of these relationships, the + /// hook is fired after the execution of this hook. + /// + /// The unique set of affected entities. + /// An enum indicating from where the hook was triggered. + void AfterUpdate(HashSet entities, ResourcePipeline pipeline); + + /// + /// Implement this hook to run custom logic in the layer + /// just after a relationship was updated. + /// + /// Relationship helper. + /// An enum indicating from where the hook was triggered. + void AfterUpdateRelationship(IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); + + /// + /// Implement this hook to run custom logic in the /// layer just before implicitly updating relationships to entities of type . /// /// This hook is fired when a relationship to entities of type @@ -138,72 +180,52 @@ public interface IBeforeHooks where TResource : class, IIdentifiable } /// - /// Wrapper interface for all After hooks. + /// Delete hooks container /// - public interface IAfterHooks where TResource : class, IIdentifiable + public interface IDeleteHookContainer where TResource : class, IIdentifiable { /// - /// Implement this hook to run custom logic in the - /// layer just after creation of entities of type . + /// Implement this hook to run custom logic in the + /// layer just before deleting entities of type . /// - /// If relationships were created with the created entities, this will - /// be reflected by the corresponding NavigationProperty being set. - /// For each of these relationships, the - /// hook is fired after the execution of this hook. - /// - /// The transformed entity set - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - void AfterCreate(HashSet entities, ResourcePipeline pipeline); - /// - /// Implement this hook to run custom logic in the - /// layer just after reading entities of type . - /// - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - /// A boolean to indicate whether the entities in this hook execution are the main entities of the request, - /// or if they were included as a relationship - void AfterRead(HashSet entities, ResourcePipeline pipeline, bool isIncluded = false); - /// - /// Implement this hook to run custom logic in the - /// layer just after updating entities of type . + /// For the pipeline, + /// will typically contain one entity. /// - /// If relationships were updated with the updated entities, this will - /// be reflected by the corresponding NavigationProperty being set. - /// For each of these relationships, the - /// hook is fired after the execution of this hook. + /// The returned may be a subset + /// of , in which case the operation of the + /// pipeline will not be executed for the omitted entities. + /// + /// If by the deletion of these entities any other entities are affected + /// implicitly by the removal of their relationships (eg + /// in the case of an one-to-one relationship), the + /// hook is fired for these entities. /// + /// The transformed entity set /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. - void AfterUpdate(HashSet entities, ResourcePipeline pipeline); + IEnumerable BeforeDelete(IEntityHashSet entities, ResourcePipeline pipeline); + /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just after deletion of entities of type . /// /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. /// If set to true if the deletion was succeeded in the repository layer. void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded); - /// - /// Implement this hook to run custom logic in the layer - /// just after a relationship was updated. - /// - /// Relationship helper. - /// An enum indicating from where the hook was triggered. - void AfterUpdateRelationship(IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); } /// - /// Wrapper interface for all on hooks. + /// On return hook container /// - public interface IOnHooks where TResource : class, IIdentifiable + public interface IOnReturnHookContainer where TResource : class, IIdentifiable { /// /// Implement this hook to transform the result data just before returning /// the entities of type from the - /// layer + /// layer /// - /// The returned may be a subset + /// The returned may be a subset /// of and may contain changes in properties /// of the encapsulated entities. /// diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs index e642e6b9a4..44ab2929bf 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs @@ -7,23 +7,20 @@ namespace JsonApiDotNetCore.Hooks /// /// Transient service responsible for executing Resource Hooks as defined /// in . see methods in - /// , and - /// for more information. + /// , and + /// for more information. /// /// Uses for traversal of nested entity data structures. /// Uses for retrieving meta data about hooks, /// fetching database values and performing other recurring internal operations. /// - public interface IResourceHookExecutor : IBeforeExecutor, IAfterExecutor, IOnExecutor { } + public interface IResourceHookExecutor : IReadHookExecutor, IUpdateHookExecutor, ICreateHookExecutor, IDeleteHookExecutor, IOnReturnHookExecutor { } - /// - /// Wrapper interface for all Before execution methods. - /// - public interface IBeforeExecutor + public interface ICreateHookExecutor { /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. - /// The returned set will be used in the actual operation in . + /// The returned set will be used in the actual operation in . /// /// Fires the /// hook where T = for values in parameter . @@ -37,41 +34,25 @@ public interface IBeforeExecutor /// The type of the root entities IEnumerable BeforeCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// - /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. - /// - /// Fires the - /// hook where T = for the requested - /// entities as well as any related relationship. - /// - /// An enum indicating from where the hook was triggered. - /// StringId of the requested entity in the case of - /// . - /// The type of the request entity - void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TResource : class, IIdentifiable; - /// - /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. - /// The returned set will be used in the actual operation in . + /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// - /// Fires the + /// Fires the /// hook where T = for values in parameter . /// - /// Fires the + /// Fires the /// hook for any related (nested) entity for values within parameter - /// - /// Fires the - /// hook for any entities that are indirectly (implicitly) affected by this operation. - /// Eg: when updating a one-to-one relationship of an entity which already - /// had this relationship populated, then this update will indirectly affect - /// the existing relationship value. /// - /// The transformed set /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. /// The type of the root entities - IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + } + + public interface IDeleteHookExecutor + { /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. - /// The returned set will be used in the actual operation in . + /// The returned set will be used in the actual operation in . /// /// Fires the /// hook where T = for values in parameter . @@ -86,26 +67,35 @@ public interface IBeforeExecutor /// An enum indicating from where the hook was triggered. /// The type of the root entities IEnumerable BeforeDelete(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + /// + /// Executes the After Cycle by firing the appropiate hooks if they are implemented. + /// + /// Fires the + /// hook where T = for values in parameter . + /// + /// Target entities for the Before cycle. + /// An enum indicating from where the hook was triggered. + /// The type of the root entities + void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TResource : class, IIdentifiable; } /// - /// Wrapper interface for all After execution methods. + /// Wrapper interface for all Before execution methods. /// - public interface IAfterExecutor + public interface IReadHookExecutor { /// - /// Executes the After Cycle by firing the appropiate hooks if they are implemented. - /// - /// Fires the - /// hook where T = for values in parameter . + /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. /// - /// Fires the - /// hook for any related (nested) entity for values within parameter + /// Fires the + /// hook where T = for the requested + /// entities as well as any related relationship. /// - /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + /// StringId of the requested entity in the case of + /// . + /// The type of the request entity + void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TResource : class, IIdentifiable; /// /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// @@ -116,35 +106,53 @@ public interface IAfterExecutor /// An enum indicating from where the hook was triggered. /// The type of the root entities void AfterRead(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + } + + /// + /// Wrapper interface for all After execution methods. + /// + public interface IUpdateHookExecutor + { /// - /// Executes the After Cycle by firing the appropiate hooks if they are implemented. + /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. + /// The returned set will be used in the actual operation in . /// - /// Fires the + /// Fires the /// hook where T = for values in parameter . /// - /// Fires the + /// Fires the /// hook for any related (nested) entity for values within parameter + /// + /// Fires the + /// hook for any entities that are indirectly (implicitly) affected by this operation. + /// Eg: when updating a one-to-one relationship of an entity which already + /// had this relationship populated, then this update will indirectly affect + /// the existing relationship value. /// + /// The transformed set /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. /// The type of the root entities - void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// - /// Fires the + /// Fires the /// hook where T = for values in parameter . + /// + /// Fires the + /// hook for any related (nested) entity for values within parameter /// /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. /// The type of the root entities - void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TResource : class, IIdentifiable; + void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; } /// /// Wrapper interface for all On execution methods. /// - public interface IOnExecutor + public interface IOnReturnHookExecutor { /// /// Executes the On Cycle by firing the appropiate hooks if they are implemented. diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index b351a2a92d..f8e4dad3ac 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -5,8 +5,8 @@ using System.Reflection; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; -using PrincipalType = System.Type; -using DependentType = System.Type; +using LeftType = System.Type; +using RightType = System.Type; using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Serialization; @@ -37,24 +37,24 @@ public ResourceHookExecutor( } /// - public virtual void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TEntity : class, IIdentifiable + public virtual void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TResource : class, IIdentifiable { - var hookContainer = _executorHelper.GetResourceHookContainer(ResourceHook.BeforeRead); + var hookContainer = _executorHelper.GetResourceHookContainer(ResourceHook.BeforeRead); hookContainer?.BeforeRead(pipeline, false, stringId); - var calledContainers = new List() { typeof(TEntity) }; + var calledContainers = new List() { typeof(TResource) }; foreach (var chain in _includeService.Get()) RecursiveBeforeRead(chain, pipeline, calledContainers); } /// - public virtual IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.BeforeUpdate, entities, out var container, out var node)) { var relationships = node.RelationshipsToNextLayer.Select(p => p.Attribute).ToArray(); - var dbValues = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, relationships); - var diff = new DiffableEntityHashSet(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer(), _targetedFields); - IEnumerable updated = container.BeforeUpdate(diff, pipeline); + var dbValues = LoadDbValues(typeof(TResource), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, relationships); + var diff = new DiffableEntityHashSet(node.UniqueEntities, dbValues, node.LeftsToNextLayer(), _targetedFields); + IEnumerable updated = container.BeforeUpdate(diff, pipeline); node.UpdateUnique(updated); node.Reassign(entities); } @@ -64,12 +64,12 @@ public virtual IEnumerable BeforeUpdate(IEnumerable e } /// - public virtual IEnumerable BeforeCreate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual IEnumerable BeforeCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.BeforeCreate, entities, out var container, out var node)) { - var affected = new EntityHashSet((HashSet)node.UniqueEntities, node.PrincipalsToNextLayer()); - IEnumerable updated = container.BeforeCreate(affected, pipeline); + var affected = new EntityHashSet((HashSet)node.UniqueEntities, node.LeftsToNextLayer()); + IEnumerable updated = container.BeforeCreate(affected, pipeline); node.UpdateUnique(updated); node.Reassign(entities); } @@ -78,15 +78,15 @@ public virtual IEnumerable BeforeCreate(IEnumerable e } /// - public virtual IEnumerable BeforeDelete(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual IEnumerable BeforeDelete(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.BeforeDelete, entities, out var container, out var node)) { var relationships = node.RelationshipsToNextLayer.Select(p => p.Attribute).ToArray(); - var targetEntities = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, relationships) ?? node.UniqueEntities; - var affected = new EntityHashSet(targetEntities, node.PrincipalsToNextLayer()); + var targetEntities = LoadDbValues(typeof(TResource), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, relationships) ?? node.UniqueEntities; + var affected = new EntityHashSet(targetEntities, node.LeftsToNextLayer()); - IEnumerable updated = container.BeforeDelete(affected, pipeline); + IEnumerable updated = container.BeforeDelete(affected, pipeline); node.UpdateUnique(updated); node.Reassign(entities); } @@ -95,21 +95,21 @@ public virtual IEnumerable BeforeDelete(IEnumerable e /// Here we're loading all relations onto the to-be-deleted article /// if for that relation the BeforeImplicitUpdateHook is implemented, /// and this hook is then executed - foreach (var entry in node.PrincipalsToNextLayerByRelationships()) + foreach (var entry in node.LeftsToNextLayerByRelationships()) { - var dependentType = entry.Key; + var rightType = entry.Key; var implicitTargets = entry.Value; - FireForAffectedImplicits(dependentType, implicitTargets, pipeline); + FireForAffectedImplicits(rightType, implicitTargets, pipeline); } return entities; } /// - public virtual IEnumerable OnReturn(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual IEnumerable OnReturn(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.OnReturn, entities, out var container, out var node) && pipeline != ResourcePipeline.GetRelationship) { - IEnumerable updated = container.OnReturn((HashSet)node.UniqueEntities, pipeline); + IEnumerable updated = container.OnReturn((HashSet)node.UniqueEntities, pipeline); ValidateHookResponse(updated); node.UpdateUnique(updated); node.Reassign(entities); @@ -125,11 +125,11 @@ public virtual IEnumerable OnReturn(IEnumerable entit } /// - public virtual void AfterRead(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual void AfterRead(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.AfterRead, entities, out var container, out var node)) { - container.AfterRead((HashSet)node.UniqueEntities, pipeline); + container.AfterRead((HashSet)node.UniqueEntities, pipeline); } Traverse(_traversalHelper.CreateNextLayer(node), ResourceHook.AfterRead, (nextContainer, nextNode) => @@ -139,11 +139,11 @@ public virtual void AfterRead(IEnumerable entities, ResourcePi } /// - public virtual void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.AfterCreate, entities, out var container, out var node)) { - container.AfterCreate((HashSet)node.UniqueEntities, pipeline); + container.AfterCreate((HashSet)node.UniqueEntities, pipeline); } Traverse(_traversalHelper.CreateNextLayer(node), @@ -152,11 +152,11 @@ public virtual void AfterCreate(IEnumerable entities, Resource } /// - public virtual void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.AfterUpdate, entities, out var container, out var node)) { - container.AfterUpdate((HashSet)node.UniqueEntities, pipeline); + container.AfterUpdate((HashSet)node.UniqueEntities, pipeline); } Traverse(_traversalHelper.CreateNextLayer(node), @@ -165,28 +165,28 @@ public virtual void AfterUpdate(IEnumerable entities, Resource } /// - public virtual void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TEntity : class, IIdentifiable + public virtual void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.AfterDelete, entities, out var container, out var node)) { - container.AfterDelete((HashSet)node.UniqueEntities, pipeline, succeeded); + container.AfterDelete((HashSet)node.UniqueEntities, pipeline, succeeded); } } /// /// For a given target and for a given type - /// , gets the hook container if the target + /// , gets the hook container if the target /// hook was implemented and should be executed. /// /// Along the way, creates a traversable node from the root entity set. /// /// true, if hook was implemented, false otherwise. - bool GetHook(ResourceHook target, IEnumerable entities, - out IResourceHookContainer container, - out RootNode node) where TEntity : class, IIdentifiable + bool GetHook(ResourceHook target, IEnumerable entities, + out IResourceHookContainer container, + out RootNode node) where TResource : class, IIdentifiable { node = _traversalHelper.CreateRootNode(entities); - container = _executorHelper.GetResourceHookContainer(target); + container = _executorHelper.GetResourceHookContainer(target); return container != null; } @@ -198,7 +198,7 @@ void Traverse(NodeLayer currentLayer, ResourceHook target, Action - void RecursiveBeforeRead(List relationshipChain, ResourcePipeline pipeline, List calledContainers) + void RecursiveBeforeRead(List relationshipChain, ResourcePipeline pipeline, List calledContainers) { var relationship = relationshipChain.First(); - if (!calledContainers.Contains(relationship.DependentType)) + if (!calledContainers.Contains(relationship.RightType)) { - calledContainers.Add(relationship.DependentType); - var container = _executorHelper.GetResourceHookContainer(relationship.DependentType, ResourceHook.BeforeRead); + calledContainers.Add(relationship.RightType); + var container = _executorHelper.GetResourceHookContainer(relationship.RightType, ResourceHook.BeforeRead); if (container != null) CallHook(container, ResourceHook.BeforeRead, new object[] { pipeline, true, null }); } @@ -241,9 +241,9 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) { foreach (INode node in layer) { - var nestedHookcontainer = _executorHelper.GetResourceHookContainer(node.EntityType, ResourceHook.BeforeUpdateRelationship); + var nestedHookcontainer = _executorHelper.GetResourceHookContainer(node.ResourceType, ResourceHook.BeforeUpdateRelationship); IEnumerable uniqueEntities = node.UniqueEntities; - DependentType entityType = node.EntityType; + RightType entityType = node.ResourceType; Dictionary currenEntitiesGrouped; Dictionary currentEntitiesGroupedInverse; @@ -262,7 +262,7 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) /// we want want inverse relationship attribute: /// we now have the one pointing from article -> person, ] /// but we require the the one that points from person -> article - currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetDependentEntities(); + currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetRightEntities(); currentEntitiesGroupedInverse = ReplaceKeysWithInverseRelationships(currenEntitiesGrouped); var resourcesByRelationship = CreateRelationshipHelper(entityType, currentEntitiesGroupedInverse, dbValues); @@ -281,22 +281,22 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) /// To fire a hook for owner_old, we need to first get a reference to it. /// For this, we need to query the database for the HasOneAttribute:owner /// relationship of article1, which is referred to as the - /// principal side of the HasOneAttribute:owner relationship. - var principalEntities = node.RelationshipsFromPreviousLayer.GetPrincipalEntities(); - if (principalEntities.Any()) + /// left side of the HasOneAttribute:owner relationship. + var leftEntities = node.RelationshipsFromPreviousLayer.GetLeftEntities(); + if (leftEntities.Any()) { /// owner_old is loaded, which is an "implicitly affected entity" - FireForAffectedImplicits(entityType, principalEntities, pipeline, uniqueEntities); + FireForAffectedImplicits(entityType, leftEntities, pipeline, uniqueEntities); } } /// Fire the BeforeImplicitUpdateRelationship hook for article2 /// For this, we need to query the database for the current owner /// relationship value of owner_new. - currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetDependentEntities(); + currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetRightEntities(); if (currenEntitiesGrouped.Any()) { - /// dependentEntities is grouped by relationships from previous + /// rightEntities is grouped by relationships from previous /// layer, ie { HasOneAttribute:owner => owner_new }. But /// to load article2 onto owner_new, we need to have the /// RelationshipAttribute from owner to article, which is the @@ -305,9 +305,9 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) /// Note that currently in the JADNC implementation of hooks, /// the root layer is ALWAYS homogenous, so we safely assume /// that for every relationship to the previous layer, the - /// principal type is the same. - PrincipalType principalEntityType = currenEntitiesGrouped.First().Key.PrincipalType; - FireForAffectedImplicits(principalEntityType, currentEntitiesGroupedInverse, pipeline); + /// left type is the same. + LeftType leftType = currenEntitiesGrouped.First().Key.LeftType; + FireForAffectedImplicits(leftType, currentEntitiesGroupedInverse, pipeline); } } } @@ -389,7 +389,7 @@ object ThrowJsonApiExceptionOnError(Func action) /// If are included, the values of the entries in need to be replaced with these values. /// /// The relationship helper. - IRelationshipsDictionary CreateRelationshipHelper(DependentType entityType, Dictionary prevLayerRelationships, IEnumerable dbValues = null) + IRelationshipsDictionary CreateRelationshipHelper(RightType entityType, Dictionary prevLayerRelationships, IEnumerable dbValues = null) { if (dbValues != null) prevLayerRelationships = ReplaceWithDbValues(prevLayerRelationships, dbValues.Cast()); return (IRelationshipsDictionary)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipsDictionary<>), entityType, true, prevLayerRelationships); @@ -403,8 +403,8 @@ Dictionary ReplaceWithDbValues(Dictionary().Select(entity => dbValues.Single(dbEntity => dbEntity.StringId == entity.StringId)).Cast(key.PrincipalType); - prevLayerRelationships[key] = TypeHelper.CreateHashSetFor(key.PrincipalType, replaced); + var replaced = prevLayerRelationships[key].Cast().Select(entity => dbValues.Single(dbEntity => dbEntity.StringId == entity.StringId)).Cast(key.LeftType); + prevLayerRelationships[key] = TypeHelper.CreateHashSetFor(key.LeftType, replaced); } return prevLayerRelationships; } @@ -442,12 +442,12 @@ IEnumerable LoadDbValues(Type entityType, IEnumerable uniqueEntities, ResourceHo void FireAfterUpdateRelationship(IResourceHookContainer container, INode node, ResourcePipeline pipeline) { - Dictionary currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetDependentEntities(); + Dictionary currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetRightEntities(); /// the relationships attributes in currenEntitiesGrouped will be pointing from a /// resource in the previouslayer to a resource in the current (nested) layer. /// For the nested hook we need to replace these attributes with their inverse. /// See the FireNestedBeforeUpdateHooks method for a more detailed example. - var resourcesByRelationship = CreateRelationshipHelper(node.EntityType, ReplaceKeysWithInverseRelationships(currenEntitiesGrouped)); + var resourcesByRelationship = CreateRelationshipHelper(node.ResourceType, ReplaceKeysWithInverseRelationships(currenEntitiesGrouped)); CallHook(container, ResourceHook.AfterUpdateRelationship, new object[] { resourcesByRelationship, pipeline }); } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs b/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs index 8a29d6c539..00fec56681 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs @@ -4,19 +4,19 @@ using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; -using DependentType = System.Type; +using RightType = System.Type; namespace JsonApiDotNetCore.Hooks { /// /// Child node in the tree /// - /// - internal class ChildNode : INode where TEntity : class, IIdentifiable + /// + internal class ChildNode : INode where TResource : class, IIdentifiable { private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); /// - public DependentType EntityType { get; private set; } + public RightType ResourceType { get; private set; } /// public RelationshipProxy[] RelationshipsToNextLayer { get; set; } /// @@ -24,7 +24,7 @@ public IEnumerable UniqueEntities { get { - return new HashSet(_relationshipsFromPreviousLayer.SelectMany(rfpl => rfpl.DependentEntities)); + return new HashSet(_relationshipsFromPreviousLayer.SelectMany(rfpl => rfpl.RightEntities)); } } @@ -37,11 +37,11 @@ public IRelationshipsFromPreviousLayer RelationshipsFromPreviousLayer } } - private readonly RelationshipsFromPreviousLayer _relationshipsFromPreviousLayer; + private readonly RelationshipsFromPreviousLayer _relationshipsFromPreviousLayer; - public ChildNode(RelationshipProxy[] nextLayerRelationships, RelationshipsFromPreviousLayer prevLayerRelationships) + public ChildNode(RelationshipProxy[] nextLayerRelationships, RelationshipsFromPreviousLayer prevLayerRelationships) { - EntityType = typeof(TEntity); + ResourceType = typeof(TResource); RelationshipsToNextLayer = nextLayerRelationships; _relationshipsFromPreviousLayer = prevLayerRelationships; } @@ -49,10 +49,10 @@ public ChildNode(RelationshipProxy[] nextLayerRelationships, RelationshipsFromPr /// public void UpdateUnique(IEnumerable updated) { - List casted = updated.Cast().ToList(); + List casted = updated.Cast().ToList(); foreach (var rpfl in _relationshipsFromPreviousLayer) { - rpfl.DependentEntities = new HashSet(rpfl.DependentEntities.Intersect(casted, _comparer).Cast()); + rpfl.RightEntities = new HashSet(rpfl.RightEntities.Intersect(casted, _comparer).Cast()); } } @@ -62,26 +62,26 @@ public void UpdateUnique(IEnumerable updated) /// public void Reassign(IEnumerable updated = null) { - var unique = (HashSet)UniqueEntities; + var unique = (HashSet)UniqueEntities; foreach (var rfpl in _relationshipsFromPreviousLayer) { var proxy = rfpl.Proxy; - var principalEntities = rfpl.PrincipalEntities; + var leftEntities = rfpl.LeftEntities; - foreach (IIdentifiable principal in principalEntities) + foreach (IIdentifiable left in leftEntities) { - var currentValue = proxy.GetValue(principal); + var currentValue = proxy.GetValue(left); if (currentValue is IEnumerable relationshipCollection) { - var newValue = relationshipCollection.Intersect(unique, _comparer).Cast(proxy.DependentType); - proxy.SetValue(principal, newValue); + var newValue = relationshipCollection.Intersect(unique, _comparer).Cast(proxy.RightType); + proxy.SetValue(left, newValue); } else if (currentValue is IIdentifiable relationshipSingle) { if (!unique.Intersect(new HashSet() { relationshipSingle }, _comparer).Any()) { - proxy.SetValue(principal, null); + proxy.SetValue(left, null); } } } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/IEntityNode.cs b/src/JsonApiDotNetCore/Hooks/Traversal/IEntityNode.cs index 88c664a744..2519449325 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/IEntityNode.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/IEntityNode.cs @@ -1,5 +1,5 @@ using System.Collections; -using DependentType = System.Type; +using RightType = System.Type; namespace JsonApiDotNetCore.Hooks { @@ -11,7 +11,7 @@ internal interface INode /// /// Each node representes the entities of a given type throughout a particular layer. /// - DependentType EntityType { get; } + RightType ResourceType { get; } /// /// The unique set of entities in this node. Note that these are all of the same type. /// diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/ITraversalHelper.cs b/src/JsonApiDotNetCore/Hooks/Traversal/ITraversalHelper.cs index f0449b9756..2a93cbb4b0 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/ITraversalHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/ITraversalHelper.cs @@ -24,7 +24,7 @@ internal interface ITraversalHelper /// /// The root node. /// Root entities. - /// The 1st type parameter. - RootNode CreateRootNode(IEnumerable rootEntities) where TEntity : class, IIdentifiable; + /// The 1st type parameter. + RootNode CreateRootNode(IEnumerable rootEntities) where TResource : class, IIdentifiable; } } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipGroup.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipGroup.cs index 1ed52419c7..6f34f1fb7b 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipGroup.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipGroup.cs @@ -6,19 +6,19 @@ namespace JsonApiDotNetCore.Hooks internal interface IRelationshipGroup { RelationshipProxy Proxy { get; } - HashSet PrincipalEntities { get; } + HashSet LeftEntities { get; } } - internal class RelationshipGroup : IRelationshipGroup where TDependent : class, IIdentifiable + internal class RelationshipGroup : IRelationshipGroup where TRight : class, IIdentifiable { public RelationshipProxy Proxy { get; } - public HashSet PrincipalEntities { get; } - public HashSet DependentEntities { get; internal set; } - public RelationshipGroup(RelationshipProxy proxy, HashSet principalEntities, HashSet dependentEntities) + public HashSet LeftEntities { get; } + public HashSet RightEntities { get; internal set; } + public RelationshipGroup(RelationshipProxy proxy, HashSet leftEntities, HashSet rightEntities) { Proxy = proxy; - PrincipalEntities = principalEntities; - DependentEntities = dependentEntities; + LeftEntities = leftEntities; + RightEntities = rightEntities; } } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs index 6e5d90ecc8..427dbaf2eb 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs @@ -16,8 +16,6 @@ namespace JsonApiDotNetCore.Hooks /// (eg ArticleTags) is identifiable (in which case we will traverse through /// it and fire hooks for it, if defined) or not (in which case we skip /// ArticleTags and go directly to Tags. - /// - /// TODO: We can consider moving fields like DependentType and PrincipalType /// public class RelationshipProxy { @@ -30,20 +28,20 @@ public class RelationshipProxy /// For HasManyThrough it is either the ThroughProperty (when the jointable is /// Identifiable) or it is the righthand side (when the jointable is not identifiable) /// - public Type DependentType { get; private set; } - public Type PrincipalType { get { return Attribute.PrincipalType; } } + public Type RightType { get; private set; } + public Type LeftType { get { return Attribute.LeftType; } } public bool IsContextRelation { get; private set; } public RelationshipAttribute Attribute { get; set; } public RelationshipProxy(RelationshipAttribute attr, Type relatedType, bool isContextRelation) { - DependentType = relatedType; + RightType = relatedType; Attribute = attr; IsContextRelation = isContextRelation; if (attr is HasManyThroughAttribute throughAttr) { _isHasManyThrough = true; - _skipJoinTable |= DependentType != throughAttr.ThroughType; + _skipJoinTable |= RightType != throughAttr.ThroughType; } } @@ -63,22 +61,17 @@ public object GetValue(IIdentifiable entity) { return throughAttr.ThroughProperty.GetValue(entity); } - else - { - var collection = new List(); - var joinEntities = (IList)throughAttr.ThroughProperty.GetValue(entity); - if (joinEntities == null) return null; - - foreach (var joinEntity in joinEntities) - { - var rightEntity = (IIdentifiable)throughAttr.RightProperty.GetValue(joinEntity); - if (rightEntity == null) continue; - collection.Add(rightEntity); - } - return collection; + var collection = new List(); + var joinEntities = (IList)throughAttr.ThroughProperty.GetValue(entity); + if (joinEntities == null) return null; + foreach (var joinEntity in joinEntities) + { + var rightEntity = (IIdentifiable)throughAttr.RightProperty.GetValue(joinEntity); + if (rightEntity == null) continue; + collection.Add(rightEntity); } - + return collection; } return Attribute.GetValue(entity); } @@ -97,27 +90,24 @@ public void SetValue(IIdentifiable entity, object value) if (!_skipJoinTable) { var list = (IEnumerable)value; - ((HasManyThroughAttribute)Attribute).ThroughProperty.SetValue(entity, list.Cast(DependentType)); + ((HasManyThroughAttribute)Attribute).ThroughProperty.SetValue(entity, list.Cast(RightType)); return; } - else + var throughAttr = (HasManyThroughAttribute)Attribute; + var joinEntities = (IEnumerable)throughAttr.ThroughProperty.GetValue(entity); + + var filteredList = new List(); + var rightEntities = ((IEnumerable)value).Cast(RightType); + foreach (var je in joinEntities) { - var throughAttr = (HasManyThroughAttribute)Attribute; - var joinEntities = (IEnumerable)throughAttr.ThroughProperty.GetValue(entity); - var filteredList = new List(); - var rightEntities = ((IEnumerable)value).Cast(DependentType); - foreach (var je in joinEntities) + if (((IList)rightEntities).Contains(throughAttr.RightProperty.GetValue(je))) { - - if (((IList)rightEntities).Contains(throughAttr.RightProperty.GetValue(je))) - { - filteredList.Add(je); - } + filteredList.Add(je); } - throughAttr.ThroughProperty.SetValue(entity, filteredList.Cast(throughAttr.ThroughType)); - return; } + throughAttr.ThroughProperty.SetValue(entity, filteredList.Cast(throughAttr.ThroughType)); + return; } Attribute.SetValue(entity, value); } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs index cb4185d1fd..2068fa8652 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs @@ -13,37 +13,39 @@ internal interface IRelationshipsFromPreviousLayer /// /// Grouped by relationship to the previous layer, gets all the entities of the current layer /// - /// The dependent entities. - Dictionary GetDependentEntities(); + /// The right side entities. + Dictionary GetRightEntities(); /// /// Grouped by relationship to the previous layer, gets all the entities of the previous layer /// - /// The dependent entities. - Dictionary GetPrincipalEntities(); + /// The right side entities. + Dictionary GetLeftEntities(); } - internal class RelationshipsFromPreviousLayer : IRelationshipsFromPreviousLayer, IEnumerable> where TDependent : class, IIdentifiable + internal class RelationshipsFromPreviousLayer : IRelationshipsFromPreviousLayer, IEnumerable> where TRightResource : class, IIdentifiable { - readonly IEnumerable> _collection; + readonly IEnumerable> _collection; - public RelationshipsFromPreviousLayer(IEnumerable> collection) + public RelationshipsFromPreviousLayer(IEnumerable> collection) { _collection = collection; } - public Dictionary GetDependentEntities() + /// + public Dictionary GetRightEntities() { - return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.DependentEntities); + return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.RightEntities); } - public Dictionary GetPrincipalEntities() + /// + public Dictionary GetLeftEntities() { - return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.PrincipalEntities); + return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.LeftEntities); } - public IEnumerator> GetEnumerator() + public IEnumerator> GetEnumerator() { - return _collection.Cast>().GetEnumerator(); + return _collection.Cast>().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs index 1c4d4d6c1a..23fc32c8bb 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs @@ -7,31 +7,30 @@ namespace JsonApiDotNetCore.Hooks { - /// /// The root node class of the breadth-first-traversal of entity data structures /// as performed by the /// - internal class RootNode : INode where TEntity : class, IIdentifiable + internal class RootNode : INode where TResource : class, IIdentifiable { private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); private readonly RelationshipProxy[] _allRelationshipsToNextLayer; - private HashSet _uniqueEntities; - public Type EntityType { get; internal set; } + private HashSet _uniqueEntities; + public Type ResourceType { get; internal set; } public IEnumerable UniqueEntities { get { return _uniqueEntities; } } public RelationshipProxy[] RelationshipsToNextLayer { get; } - public Dictionary> PrincipalsToNextLayerByRelationships() + public Dictionary> LeftsToNextLayerByRelationships() { return _allRelationshipsToNextLayer - .GroupBy(proxy => proxy.DependentType) + .GroupBy(proxy => proxy.RightType) .ToDictionary(gdc => gdc.Key, gdc => gdc.ToDictionary(p => p.Attribute, p => UniqueEntities)); } /// /// The current layer entities grouped by affected relationship to the next layer /// - public Dictionary PrincipalsToNextLayer() + public Dictionary LeftsToNextLayer() { return RelationshipsToNextLayer.ToDictionary(p => p.Attribute, p => UniqueEntities); } @@ -41,10 +40,10 @@ public Dictionary PrincipalsToNextLayer() /// public IRelationshipsFromPreviousLayer RelationshipsFromPreviousLayer { get { return null; } } - public RootNode(IEnumerable uniqueEntities, RelationshipProxy[] poplatedRelationships, RelationshipProxy[] allRelationships) + public RootNode(IEnumerable uniqueEntities, RelationshipProxy[] poplatedRelationships, RelationshipProxy[] allRelationships) { - EntityType = typeof(TEntity); - _uniqueEntities = new HashSet(uniqueEntities); + ResourceType = typeof(TResource); + _uniqueEntities = new HashSet(uniqueEntities); RelationshipsToNextLayer = poplatedRelationships; _allRelationshipsToNextLayer = allRelationships; } @@ -55,15 +54,15 @@ public RootNode(IEnumerable uniqueEntities, RelationshipProxy[] poplate /// Updated. public void UpdateUnique(IEnumerable updated) { - var casted = updated.Cast().ToList(); - var intersected = _uniqueEntities.Intersect(casted, _comparer).Cast(); - _uniqueEntities = new HashSet(intersected); + var casted = updated.Cast().ToList(); + var intersected = _uniqueEntities.Intersect(casted, _comparer).Cast(); + _uniqueEntities = new HashSet(intersected); } public void Reassign(IEnumerable source = null) { var ids = _uniqueEntities.Select(ue => ue.StringId); - ((List)source).RemoveAll(se => !ids.Contains(se.StringId)); + ((List)source).RemoveAll(se => !ids.Contains(se.StringId)); } } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs index 80c6d797ca..1b8fb9c935 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs @@ -6,11 +6,10 @@ using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Contracts; -using JsonApiDotNetCore.Managers.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Serialization; -using DependentType = System.Type; -using PrincipalType = System.Type; +using RightType = System.Type; +using LeftType = System.Type; namespace JsonApiDotNetCore.Hooks { @@ -31,12 +30,12 @@ internal class TraversalHelper : ITraversalHelper /// Keeps track of which entities has already been traversed through, to prevent /// infinite loops in eg cyclic data structures. /// - private Dictionary> _processedEntities; + private Dictionary> _processedEntities; /// /// A mapper from to . /// See the latter for more details. /// - private readonly Dictionary RelationshipProxies = new Dictionary(); + private readonly Dictionary _relationshipProxies = new Dictionary(); public TraversalHelper( IResourceGraph resourceGraph, ITargetedFields targetedFields) @@ -52,15 +51,15 @@ public TraversalHelper( /// /// The root node. /// Root entities. - /// The 1st type parameter. - public RootNode CreateRootNode(IEnumerable rootEntities) where TEntity : class, IIdentifiable + /// The 1st type parameter. + public RootNode CreateRootNode(IEnumerable rootEntities) where TResource : class, IIdentifiable { - _processedEntities = new Dictionary>(); - RegisterRelationshipProxies(typeof(TEntity)); + _processedEntities = new Dictionary>(); + RegisterRelationshipProxies(typeof(TResource)); var uniqueEntities = ProcessEntities(rootEntities); - var populatedRelationshipsToNextLayer = GetPopulatedRelationships(typeof(TEntity), uniqueEntities.Cast()); - var allRelationshipsFromType = RelationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.PrincipalType == typeof(TEntity)).ToArray(); - return new RootNode(uniqueEntities, populatedRelationshipsToNextLayer, allRelationshipsFromType); + var populatedRelationshipsToNextLayer = GetPopulatedRelationships(typeof(TResource), uniqueEntities.Cast()); + var allRelationshipsFromType = _relationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.LeftType == typeof(TResource)).ToArray(); + return new RootNode(uniqueEntities, populatedRelationshipsToNextLayer, allRelationshipsFromType); } /// @@ -82,15 +81,15 @@ public NodeLayer CreateNextLayer(IEnumerable nodes) { /// first extract entities by parsing populated relationships in the entities /// of previous layer - (var principals, var dependents) = ExtractEntities(nodes); + (var lefts, var rights) = ExtractEntities(nodes); /// group them conveniently so we can make ChildNodes of them: - /// there might be several relationship attributes in dependents dictionary - /// that point to the same dependent type. - var principalsGrouped = GroupByDependentTypeOfRelationship(principals); + /// there might be several relationship attributes in rights dictionary + /// that point to the same right type. + var leftsGrouped = GroupByRightTypeOfRelationship(lefts); /// convert the groups into child nodes - var nextNodes = principalsGrouped.Select(entry => + var nextNodes = leftsGrouped.Select(entry => { var nextNodeType = entry.Key; RegisterRelationshipProxies(nextNodeType); @@ -99,8 +98,8 @@ public NodeLayer CreateNextLayer(IEnumerable nodes) var relationshipsToPreviousLayer = entry.Value.Select(grouped => { var proxy = grouped.Key; - populatedRelationships.AddRange(GetPopulatedRelationships(nextNodeType, dependents[proxy])); - return CreateRelationshipGroupInstance(nextNodeType, proxy, grouped.Value, dependents[proxy]); + populatedRelationships.AddRange(GetPopulatedRelationships(nextNodeType, rights[proxy])); + return CreateRelationshipGroupInstance(nextNodeType, proxy, grouped.Value, rights[proxy]); }).ToList(); return CreateNodeInstance(nextNodeType, populatedRelationships.ToArray(), relationshipsToPreviousLayer); @@ -112,72 +111,72 @@ public NodeLayer CreateNextLayer(IEnumerable nodes) /// /// iterates throug the dictinary and groups the values - /// by matching dependent type of the keys (which are relationshipattributes) + /// by matching right type of the keys (which are relationshipattributes) /// - Dictionary>>> GroupByDependentTypeOfRelationship(Dictionary> relationships) + Dictionary>>> GroupByRightTypeOfRelationship(Dictionary> relationships) { - return relationships.GroupBy(kvp => kvp.Key.DependentType).ToDictionary(gdc => gdc.Key, gdc => gdc.ToList()); + return relationships.GroupBy(kvp => kvp.Key.RightType).ToDictionary(gdc => gdc.Key, gdc => gdc.ToList()); } /// /// Extracts the entities for the current layer by going through all populated relationships - /// of the (principal entities of the previous layer. + /// of the (left entities of the previous layer. /// - (Dictionary>, Dictionary>) ExtractEntities(IEnumerable principalNodes) + (Dictionary>, Dictionary>) ExtractEntities(IEnumerable leftNodes) { - var principalsEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => prevLayerEntities - var dependentsEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => currentLayerEntities + var leftEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => prevLayerEntities + var rightEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => currentLayerEntities - foreach (var node in principalNodes) + foreach (var node in leftNodes) { - var principalEntities = node.UniqueEntities; + var leftEntities = node.UniqueEntities; var relationships = node.RelationshipsToNextLayer; - foreach (IIdentifiable principalEntity in principalEntities) + foreach (IIdentifiable leftEntity in leftEntities) { foreach (var proxy in relationships) { - var relationshipValue = proxy.GetValue(principalEntity); + var relationshipValue = proxy.GetValue(leftEntity); // skip this relationship if it's not populated if (!proxy.IsContextRelation && relationshipValue == null) continue; - if (!(relationshipValue is IEnumerable dependentEntities)) + if (!(relationshipValue is IEnumerable rightEntities)) { // in the case of a to-one relationship, the assigned value // will not be a list. We therefore first wrap it in a list. - var list = TypeHelper.CreateListFor(proxy.DependentType); + var list = TypeHelper.CreateListFor(proxy.RightType); if (relationshipValue != null) list.Add(relationshipValue); - dependentEntities = list; + rightEntities = list; } - var uniqueDependentEntities = UniqueInTree(dependentEntities.Cast(), proxy.DependentType); - if (proxy.IsContextRelation || uniqueDependentEntities.Any()) + var uniqueRightEntities = UniqueInTree(rightEntities.Cast(), proxy.RightType); + if (proxy.IsContextRelation || uniqueRightEntities.Any()) { - AddToRelationshipGroup(dependentsEntitiesGrouped, proxy, uniqueDependentEntities); - AddToRelationshipGroup(principalsEntitiesGrouped, proxy, new IIdentifiable[] { principalEntity }); + AddToRelationshipGroup(rightEntitiesGrouped, proxy, uniqueRightEntities); + AddToRelationshipGroup(leftEntitiesGrouped, proxy, new IIdentifiable[] { leftEntity }); } } } } var processEntitiesMethod = GetType().GetMethod(nameof(ProcessEntities), BindingFlags.NonPublic | BindingFlags.Instance); - foreach (var kvp in dependentsEntitiesGrouped) + foreach (var kvp in rightEntitiesGrouped) { - var type = kvp.Key.DependentType; + var type = kvp.Key.RightType; var list = kvp.Value.Cast(type); processEntitiesMethod.MakeGenericMethod(type).Invoke(this, new object[] { list }); } - return (principalsEntitiesGrouped, dependentsEntitiesGrouped); + return (leftEntitiesGrouped, rightEntitiesGrouped); } /// /// Get all populated relationships known in the current tree traversal from a - /// principal type to any dependent type + /// left type to any right type /// /// The relationships. - RelationshipProxy[] GetPopulatedRelationships(PrincipalType principalType, IEnumerable principals) + RelationshipProxy[] GetPopulatedRelationships(LeftType leftType, IEnumerable lefts) { - var relationshipsFromPrincipalToDependent = RelationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.PrincipalType == principalType); - return relationshipsFromPrincipalToDependent.Where(proxy => proxy.IsContextRelation || principals.Any(p => proxy.GetValue(p) != null)).ToArray(); + var relationshipsFromLeftToRight = _relationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.LeftType == leftType); + return relationshipsFromLeftToRight.Where(proxy => proxy.IsContextRelation || lefts.Any(p => proxy.GetValue(p) != null)).ToArray(); } /// @@ -185,10 +184,10 @@ RelationshipProxy[] GetPopulatedRelationships(PrincipalType principalType, IEnum /// /// The entities. /// Incoming entities. - /// The 1st type parameter. - HashSet ProcessEntities(IEnumerable incomingEntities) where TEntity : class, IIdentifiable + /// The 1st type parameter. + HashSet ProcessEntities(IEnumerable incomingEntities) where TResource : class, IIdentifiable { - Type type = typeof(TEntity); + Type type = typeof(TResource); var newEntities = UniqueInTree(incomingEntities, type); RegisterProcessedEntities(newEntities, type); return newEntities; @@ -199,24 +198,23 @@ HashSet ProcessEntities(IEnumerable incomingEntities) /// other models in the resource resourceGraphs by constructing RelationshipProxies . /// /// The type to parse - void RegisterRelationshipProxies(DependentType type) + void RegisterRelationshipProxies(RightType type) { foreach (RelationshipAttribute attr in _resourceGraph.GetRelationships(type)) { if (!attr.CanInclude) continue; - if (!RelationshipProxies.TryGetValue(attr, out RelationshipProxy proxies)) + if (!_relationshipProxies.TryGetValue(attr, out RelationshipProxy proxies)) { - DependentType dependentType = GetDependentTypeFromRelationship(attr); + RightType rightType = GetRightTypeFromRelationship(attr); bool isContextRelation = false; var relationshipsToUpdate = _targetedFields.Relationships; if (relationshipsToUpdate != null) isContextRelation = relationshipsToUpdate.Contains(attr); - var proxy = new RelationshipProxy(attr, dependentType, isContextRelation); - RelationshipProxies[attr] = proxy; + var proxy = new RelationshipProxy(attr, rightType, isContextRelation); + _relationshipProxies[attr] = proxy; } } } - /// /// Registers the processed entities in the dictionary grouped by type /// @@ -250,10 +248,10 @@ HashSet GetProcessedEntities(Type entityType) /// The in tree. /// Entities. /// Entity type. - HashSet UniqueInTree(IEnumerable entities, Type entityType) where TEntity : class, IIdentifiable + HashSet UniqueInTree(IEnumerable entities, Type entityType) where TResource : class, IIdentifiable { - var newEntities = entities.Except(GetProcessedEntities(entityType), _comparer).Cast(); - return new HashSet(newEntities); + var newEntities = entities.Except(GetProcessedEntities(entityType), _comparer).Cast(); + return new HashSet(newEntities); } /// @@ -264,13 +262,13 @@ HashSet UniqueInTree(IEnumerable entities, Type entit /// /// The target type for traversal /// Relationship attribute - DependentType GetDependentTypeFromRelationship(RelationshipAttribute attr) + RightType GetRightTypeFromRelationship(RelationshipAttribute attr) { if (attr is HasManyThroughAttribute throughAttr && throughAttr.ThroughType.Inherits(typeof(IIdentifiable))) { return throughAttr.ThroughType; } - return attr.DependentType; + return attr.RightType; } void AddToRelationshipGroup(Dictionary> target, RelationshipProxy proxy, IEnumerable newEntities) @@ -284,32 +282,32 @@ void AddToRelationshipGroup(Dictionary> t } /// - /// Reflective helper method to create an instance of ; + /// Reflective helper method to create an instance of ; /// - INode CreateNodeInstance(DependentType nodeType, RelationshipProxy[] relationshipsToNext, IEnumerable relationshipsFromPrev) + INode CreateNodeInstance(RightType nodeType, RelationshipProxy[] relationshipsToNext, IEnumerable relationshipsFromPrev) { IRelationshipsFromPreviousLayer prev = CreateRelationshipsFromInstance(nodeType, relationshipsFromPrev); return (INode)TypeHelper.CreateInstanceOfOpenType(typeof(ChildNode<>), nodeType, new object[] { relationshipsToNext, prev }); } /// - /// Reflective helper method to create an instance of ; + /// Reflective helper method to create an instance of ; /// - IRelationshipsFromPreviousLayer CreateRelationshipsFromInstance(DependentType nodeType, IEnumerable relationshipsFromPrev) + IRelationshipsFromPreviousLayer CreateRelationshipsFromInstance(RightType nodeType, IEnumerable relationshipsFromPrev) { var casted = relationshipsFromPrev.Cast(relationshipsFromPrev.First().GetType()); return (IRelationshipsFromPreviousLayer)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipsFromPreviousLayer<>), nodeType, new object[] { casted }); } /// - /// Reflective helper method to create an instance of ; + /// Reflective helper method to create an instance of ; /// - IRelationshipGroup CreateRelationshipGroupInstance(Type thisLayerType, RelationshipProxy proxy, List principalEntities, List dependentEntities) + IRelationshipGroup CreateRelationshipGroupInstance(Type thisLayerType, RelationshipProxy proxy, List leftEntities, List rightEntities) { - var dependentEntitiesHashed = TypeHelper.CreateInstanceOfOpenType(typeof(HashSet<>), thisLayerType, dependentEntities.Cast(thisLayerType)); + var rightEntitiesHashed = TypeHelper.CreateInstanceOfOpenType(typeof(HashSet<>), thisLayerType, rightEntities.Cast(thisLayerType)); return (IRelationshipGroup)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipGroup<>), thisLayerType, - new object[] { proxy, new HashSet(principalEntities), dependentEntitiesHashed }); + new object[] { proxy, new HashSet(leftEntities), rightEntitiesHashed }); } } diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceContextProvider.cs similarity index 52% rename from src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs rename to src/JsonApiDotNetCore/Internal/Contracts/IResourceContextProvider.cs index c5bf26cc7a..c975211b6d 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceContextProvider.cs @@ -1,34 +1,31 @@ using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Internal.Contracts { /// - /// Responsible for getting s from the . + /// Responsible for getting s from the . /// - public interface IContextEntityProvider + public interface IResourceContextProvider { /// /// Gets all registered context entities /// - ContextEntity[] GetContextEntities(); + ResourceContext[] GetResourceContexts(); /// /// Get the resource metadata by the DbSet property name /// - ContextEntity GetContextEntity(string exposedResourceName); + ResourceContext GetResourceContext(string exposedResourceName); /// /// Get the resource metadata by the resource type /// - ContextEntity GetContextEntity(Type resourceType); + ResourceContext GetResourceContext(Type resourceType); /// /// Get the resource metadata by the resource type /// - ContextEntity GetContextEntity() where TResource : class, IIdentifiable; + ResourceContext GetResourceContext() where TResource : class, IIdentifiable; } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs index 550f351e8f..4768e3744c 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCore.Internal.Contracts /// Responsible for retrieving the exposed resource fields (attributes and /// relationships) of registered resources in the resource resourceGraph. /// - public interface IResourceGraph : IContextEntityProvider + public interface IResourceGraph : IResourceContextProvider { /// /// Gets all fields (attributes and relationships) for @@ -50,7 +50,6 @@ public interface IResourceGraph : IContextEntityProvider /// /// The resource type. Must extend IIdentifiable. List GetRelationships(Type type); - /// /// Traverses the resource resourceGraph for the inverse relationship of the provided /// ; diff --git a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs index ca54f13f6c..cf25305ca8 100644 --- a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs @@ -1,5 +1,3 @@ -// REF: https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.CustomRoutingConvention/NameSpaceRoutingConvention.cs -// REF: https://github.com/aspnet/Mvc/issues/5691 using System; using System.Collections.Generic; using System.Linq; diff --git a/src/JsonApiDotNetCore/Internal/Exceptions/JsonApiRouteHandler.cs b/src/JsonApiDotNetCore/Internal/Exceptions/JsonApiRouteHandler.cs deleted file mode 100644 index 132f8d2042..0000000000 --- a/src/JsonApiDotNetCore/Internal/Exceptions/JsonApiRouteHandler.cs +++ /dev/null @@ -1,83 +0,0 @@ -// REF: https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcRouteHandler.cs -using System; -using System.Threading.Tasks; -using JsonApiDotNetCore.Extensions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.AspNetCore.Routing; - -namespace JsonApiDotNetCore.Internal -{ - public class JsonApiRouteHandler : IRouter - { - private readonly IActionContextAccessor _actionContextAccessor; - private readonly IActionInvokerFactory _actionInvokerFactory; - private readonly IActionSelector _actionSelector; - - public JsonApiRouteHandler( - IActionInvokerFactory actionInvokerFactory, - IActionSelector actionSelector) - : this(actionInvokerFactory, actionSelector, actionContextAccessor: null) - { - } - - public JsonApiRouteHandler( - IActionInvokerFactory actionInvokerFactory, - IActionSelector actionSelector, - IActionContextAccessor actionContextAccessor) - { - // The IActionContextAccessor is optional. We want to avoid the overhead of using CallContext - // if possible. - _actionContextAccessor = actionContextAccessor; - _actionInvokerFactory = actionInvokerFactory; - _actionSelector = actionSelector; - } - - public VirtualPathData GetVirtualPath(VirtualPathContext context) - { - if (context == null) - throw new ArgumentNullException(nameof(context)); - - // We return null here because we're not responsible for generating the url, the route is. - return null; - } - - public Task RouteAsync(RouteContext context) - { - if (context == null) - throw new ArgumentNullException(nameof(context)); - - var candidates = _actionSelector.SelectCandidates(context); - if (candidates == null || candidates.Count == 0) - { - return Task.CompletedTask; - } - - var actionDescriptor = _actionSelector.SelectBestCandidate(context, candidates); - if (actionDescriptor == null) - { - return Task.CompletedTask; - } - - context.Handler = (c) => - { - var routeData = c.GetRouteData(); - - foreach(var routeValue in routeData.Values) - routeData.Values[routeValue.Key] = routeValue.Value.ToString().ToProperCase(); - - var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor); - if (_actionContextAccessor != null) - { - _actionContextAccessor.ActionContext = actionContext; - } - - var invoker = _actionInvokerFactory.CreateInvoker(actionContext); - - return invoker.InvokeAsync(); - }; - - return Task.CompletedTask; - } - } -} diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs deleted file mode 100644 index 5145621058..0000000000 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using JsonApiDotNetCore.Data; -using JsonApiDotNetCore.Extensions; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Services; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; - -namespace JsonApiDotNetCore.Internal.Generics -{ - // TODO: consider renaming to PatchRelationshipService (or something) - public interface IGenericProcessor - { - Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds); - } - - /// - /// A special processor that gets instantiated for a generic type (<T>) - /// when the actual type is not known until runtime. Specifically, this is used for updating - /// relationships. - /// - public class GenericProcessor : IGenericProcessor where T : class - { - private readonly DbContext _context; - public GenericProcessor(IDbContextResolver contextResolver) - { - _context = contextResolver.GetContext(); - } - - public virtual async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds) - { - if (relationship is HasManyThroughAttribute hasManyThrough && parent is IIdentifiable identifiableParent) - { - await SetHasManyThroughRelationshipAsync(identifiableParent, hasManyThrough, relationshipIds); - } - else - { - await SetRelationshipsAsync(parent, relationship, relationshipIds); - } - } - - private async Task SetHasManyThroughRelationshipAsync(IIdentifiable identifiableParent, HasManyThroughAttribute hasManyThrough, IEnumerable relationshipIds) - { - // we need to create a transaction for the HasManyThrough case so we can get and remove any existing - // join entities and only commit if all operations are successful - using(var transaction = await _context.GetCurrentOrCreateTransactionAsync()) - { - // ArticleTag - ParameterExpression parameter = Expression.Parameter(hasManyThrough.ThroughType); - - // ArticleTag.ArticleId - Expression property = Expression.Property(parameter, hasManyThrough.LeftIdProperty); - - // article.Id - var parentId = TypeHelper.ConvertType(identifiableParent.StringId, hasManyThrough.LeftIdProperty.PropertyType); - Expression target = Expression.Constant(parentId); - - // ArticleTag.ArticleId.Equals(article.Id) - Expression equals = Expression.Call(property, "Equals", null, target); - - var lambda = Expression.Lambda>(equals, parameter); - - // TODO: we shouldn't need to do this instead we should try updating the existing? - // the challenge here is if a composite key is used, then we will fail to - // create due to a unique key violation - var oldLinks = _context - .Set() - .Where(lambda.Compile()) - .ToList(); - - _context.RemoveRange(oldLinks); - - var newLinks = relationshipIds.Select(x => { - var link = Activator.CreateInstance(hasManyThrough.ThroughType); - hasManyThrough.LeftIdProperty.SetValue(link, TypeHelper.ConvertType(parentId, hasManyThrough.LeftIdProperty.PropertyType)); - hasManyThrough.RightIdProperty.SetValue(link, TypeHelper.ConvertType(x, hasManyThrough.RightIdProperty.PropertyType)); - return link; - }); - - _context.AddRange(newLinks); - await _context.SaveChangesAsync(); - - transaction.Commit(); - } - } - - private async Task SetRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds) - { - if (relationship.IsHasMany) - { - var entities = _context.Set().Where(x => relationshipIds.Contains(((IIdentifiable)x).StringId)).ToList(); - relationship.SetValue(parent, entities); - } - else - { - var entity = _context.Set().SingleOrDefault(x => relationshipIds.First() == ((IIdentifiable)x).StringId); - relationship.SetValue(parent, entity); - } - - await _context.SaveChangesAsync(); - } - } -} diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericServiceFactory.cs similarity index 50% rename from src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs rename to src/JsonApiDotNetCore/Internal/Generics/GenericServiceFactory.cs index f6849b178b..bc834f4733 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/GenericServiceFactory.cs @@ -8,45 +8,45 @@ namespace JsonApiDotNetCore.Internal.Generics /// are not known until runtime. The typical use case would be for /// accessing relationship data or resolving operations processors. /// - public interface IGenericProcessorFactory + public interface IGenericServiceFactory { /// /// Constructs the generic type and locates the service, then casts to TInterface /// /// /// - /// GetProcessor<IGenericProcessor>(typeof(GenericProcessor<>), typeof(TResource)); + /// Get<IGenericProcessor>(typeof(GenericProcessor<>), typeof(TResource)); /// /// - TInterface GetProcessor(Type openGenericType, Type resourceType); + TInterface Get(Type openGenericType, Type resourceType); /// /// Constructs the generic type and locates the service, then casts to TInterface /// /// /// - /// GetProcessor<IGenericProcessor>(typeof(GenericProcessor<,>), typeof(TResource), typeof(TId)); + /// Get<IGenericProcessor>(typeof(GenericProcessor<,>), typeof(TResource), typeof(TId)); /// /// - TInterface GetProcessor(Type openGenericType, Type resourceType, Type keyType); + TInterface Get(Type openGenericType, Type resourceType, Type keyType); } - public class GenericProcessorFactory : IGenericProcessorFactory + public class GenericServiceFactory : IGenericServiceFactory { private readonly IServiceProvider _serviceProvider; - public GenericProcessorFactory(IScopedServiceProvider serviceProvider) + public GenericServiceFactory(IScopedServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } - public TInterface GetProcessor(Type openGenericType, Type resourceType) - => GetProcessorInternal(openGenericType, resourceType); + public TInterface Get(Type openGenericType, Type resourceType) + => GetInternal(openGenericType, resourceType); - public TInterface GetProcessor(Type openGenericType, Type resourceType, Type keyType) - => GetProcessorInternal(openGenericType, resourceType, keyType); + public TInterface Get(Type openGenericType, Type resourceType, Type keyType) + => GetInternal(openGenericType, resourceType, keyType); - private TInterface GetProcessorInternal(Type openGenericType, params Type[] types) + private TInterface GetInternal(Type openGenericType, params Type[] types) { var concreteType = openGenericType.MakeGenericType(types); diff --git a/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs b/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs new file mode 100644 index 0000000000..c98560015e --- /dev/null +++ b/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using JsonApiDotNetCore.Data; +using JsonApiDotNetCore.Extensions; +using JsonApiDotNetCore.Models; +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCore.Internal.Generics +{ + /// + /// A special helper service that gets instantiated for the right-type of a many-to-many relationship and is responsible for + /// processing updates for that relationships. + /// + public interface IHasManyThroughUpdateHelper + { + /// + /// Processes updates of has many through relationship. + /// + Task UpdateAsync(IIdentifiable parent, HasManyThroughAttribute relationship, IEnumerable relationshipIds); + } + + /// + public class HasManyThroughUpdateHelper : IHasManyThroughUpdateHelper where T : class + { + private readonly DbContext _context; + public HasManyThroughUpdateHelper(IDbContextResolver contextResolver) + { + _context = contextResolver.GetContext(); + } + + /// + public virtual async Task UpdateAsync(IIdentifiable parent, HasManyThroughAttribute relationship, IEnumerable relationshipIds) + { + // we need to create a transaction for the HasManyThrough case so we can get and remove any existing + // join entities and only commit if all operations are successful + using (var transaction = await _context.GetCurrentOrCreateTransactionAsync()) + { + // ArticleTag + ParameterExpression parameter = Expression.Parameter(relationship.ThroughType); + + // ArticleTag.ArticleId + Expression property = Expression.Property(parameter, relationship.LeftIdProperty); + + // article.Id + var parentId = TypeHelper.ConvertType(parent.StringId, relationship.LeftIdProperty.PropertyType); + Expression target = Expression.Constant(parentId); + + // ArticleTag.ArticleId.Equals(article.Id) + Expression equals = Expression.Call(property, "Equals", null, target); + + var lambda = Expression.Lambda>(equals, parameter); + + // TODO: we shouldn't need to do this instead we should try updating the existing? + // the challenge here is if a composite key is used, then we will fail to + // create due to a unique key violation + var oldLinks = _context + .Set() + .Where(lambda.Compile()) + .ToList(); + + _context.RemoveRange(oldLinks); + + var newLinks = relationshipIds.Select(x => { + var link = Activator.CreateInstance(relationship.ThroughType); + relationship.LeftIdProperty.SetValue(link, TypeHelper.ConvertType(parentId, relationship.LeftIdProperty.PropertyType)); + relationship.RightIdProperty.SetValue(link, TypeHelper.ConvertType(x, relationship.RightIdProperty.PropertyType)); + return link; + }); + + _context.AddRange(newLinks); + await _context.SaveChangesAsync(); + + transaction.Commit(); + } + } + } +} diff --git a/src/JsonApiDotNetCore/Internal/IdentifiableComparer.cs b/src/JsonApiDotNetCore/Internal/IdentifiableComparer.cs index 273f5f5d51..60793829b8 100644 --- a/src/JsonApiDotNetCore/Internal/IdentifiableComparer.cs +++ b/src/JsonApiDotNetCore/Internal/IdentifiableComparer.cs @@ -1,7 +1,5 @@ using JsonApiDotNetCore.Models; -using System; using System.Collections.Generic; -using System.Text; namespace JsonApiDotNetCore.Internal { diff --git a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs index ee6b9aa249..3aa999f9bb 100644 --- a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs +++ b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs @@ -29,10 +29,10 @@ public interface IInverseRelationships /// public class InverseRelationships : IInverseRelationships { - private readonly IContextEntityProvider _provider; + private readonly IResourceContextProvider _provider; private readonly IDbContextResolver _resolver; - public InverseRelationships(IContextEntityProvider provider, IDbContextResolver resolver = null) + public InverseRelationships(IResourceContextProvider provider, IDbContextResolver resolver = null) { _provider = provider; _resolver = resolver; @@ -45,9 +45,9 @@ public void Resolve() { DbContext context = _resolver.GetContext(); - foreach (ContextEntity ce in _provider.GetContextEntities()) + foreach (ResourceContext ce in _provider.GetResourceContexts()) { - IEntityType meta = context.Model.FindEntityType(ce.EntityType); + IEntityType meta = context.Model.FindEntityType(ce.ResourceType); if (meta == null) continue; foreach (var attr in ce.Relationships) { diff --git a/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs b/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs index e3d2075d36..40588e672b 100644 --- a/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs +++ b/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs @@ -1,7 +1,3 @@ -using System; -using JsonApiDotNetCore.Extensions; -using JsonApiDotNetCore.Models; - namespace JsonApiDotNetCore.Internal.Query { /// diff --git a/src/JsonApiDotNetCore/Internal/ContextEntity.cs b/src/JsonApiDotNetCore/Internal/ResourceContext.cs similarity index 95% rename from src/JsonApiDotNetCore/Internal/ContextEntity.cs rename to src/JsonApiDotNetCore/Internal/ResourceContext.cs index 67e7d01026..91934ca070 100644 --- a/src/JsonApiDotNetCore/Internal/ContextEntity.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceContext.cs @@ -6,17 +6,17 @@ namespace JsonApiDotNetCore.Internal { - public class ContextEntity + public class ResourceContext { /// /// The exposed resource name /// - public string EntityName { get; set; } + public string ResourceName { get; set; } /// /// The data model type /// - public Type EntityType { get; set; } + public Type ResourceType { get; set; } /// /// The identity member type @@ -27,7 +27,7 @@ public class ContextEntity /// The concrete type. /// We store this so that we don't need to re-compute the generic type. /// - public Type ResourceType { get; set; } + public Type ResourceDefinitionType { get; set; } /// /// Exposed resource attributes. diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index dab6833fb1..c9cfcd2b5d 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -13,28 +13,25 @@ namespace JsonApiDotNetCore.Internal public class ResourceGraph : IResourceGraph { internal List ValidationResults { get; } - private List _entities { get; } + private List _resources { get; } - public ResourceGraph(List entities, List validationResults = null) + public ResourceGraph(List entities, List validationResults = null) { - _entities = entities; + _resources = entities; ValidationResults = validationResults; } /// - public ContextEntity[] GetContextEntities() => _entities.ToArray(); - + public ResourceContext[] GetResourceContexts() => _resources.ToArray(); /// - public ContextEntity GetContextEntity(string entityName) - => _entities.SingleOrDefault(e => string.Equals(e.EntityName, entityName, StringComparison.OrdinalIgnoreCase)); - + public ResourceContext GetResourceContext(string entityName) + => _resources.SingleOrDefault(e => string.Equals(e.ResourceName, entityName, StringComparison.OrdinalIgnoreCase)); /// - public ContextEntity GetContextEntity(Type entityType) - => _entities.SingleOrDefault(e => e.EntityType == entityType); + public ResourceContext GetResourceContext(Type entityType) + => _resources.SingleOrDefault(e => e.ResourceType == entityType); /// - public ContextEntity GetContextEntity() where TResource : class, IIdentifiable - => GetContextEntity(typeof(TResource)); - + public ResourceContext GetResourceContext() where TResource : class, IIdentifiable + => GetResourceContext(typeof(TResource)); /// public List GetFields(Expression> selector = null) where T : IIdentifiable { @@ -53,24 +50,23 @@ public List GetRelationships(Expression public List GetFields(Type type) { - return GetContextEntity(type).Fields.ToList(); + return GetResourceContext(type).Fields.ToList(); } /// public List GetAttributes(Type type) { - return GetContextEntity(type).Attributes.ToList(); + return GetResourceContext(type).Attributes.ToList(); } /// public List GetRelationships(Type type) { - return GetContextEntity(type).Relationships.ToList(); + return GetResourceContext(type).Relationships.ToList(); } - /// public RelationshipAttribute GetInverse(RelationshipAttribute relationship) { if (relationship.InverseNavigation == null) return null; - return GetContextEntity(relationship.DependentType) + return GetResourceContext(relationship.RightType) .Relationships .SingleOrDefault(r => r.InternalRelationshipName == relationship.InverseNavigation); } @@ -79,11 +75,11 @@ private IEnumerable Getter(Expression> selec { IEnumerable available; if (type == FieldFilterType.Attribute) - available = GetContextEntity(typeof(T)).Attributes.Cast(); + available = GetResourceContext(typeof(T)).Attributes.Cast(); else if (type == FieldFilterType.Relationship) - available = GetContextEntity(typeof(T)).Relationships.Cast(); + available = GetResourceContext(typeof(T)).Relationships.Cast(); else - available = GetContextEntity(typeof(T)).Fields; + available = GetResourceContext(typeof(T)).Fields; if (selector == null) return available; @@ -144,7 +140,5 @@ private enum FieldFilterType Attribute, Relationship } - - } } diff --git a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs b/src/JsonApiDotNetCore/Middleware/CurrentRequestMiddleware.cs similarity index 96% rename from src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs rename to src/JsonApiDotNetCore/Middleware/CurrentRequestMiddleware.cs index 13bffd4da0..8b8027120a 100644 --- a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs +++ b/src/JsonApiDotNetCore/Middleware/CurrentRequestMiddleware.cs @@ -12,8 +12,6 @@ namespace JsonApiDotNetCore.Middleware { /// - /// Can be overwritten to help you out during testing - /// /// This sets all necessary parameters relating to the HttpContext for JADNC /// public class CurrentRequestMiddleware @@ -46,7 +44,7 @@ public async Task Invoke(HttpContext httpContext, { _currentRequest.SetRequestResource(GetCurrentEntity()); _currentRequest.IsRelationshipPath = PathIsRelationship(); - _currentRequest.BasePath = GetBasePath(_currentRequest.GetRequestResource().EntityName); + _currentRequest.BasePath = GetBasePath(_currentRequest.GetRequestResource().ResourceName); } if (IsValid()) @@ -163,11 +161,11 @@ private void FlushResponse(HttpContext context, int statusCode) /// Gets the current entity that we need for serialization and deserialization. /// /// - private ContextEntity GetCurrentEntity() + private ResourceContext GetCurrentEntity() { var controllerName = (string)_httpContext.GetRouteData().Values["controller"]; var resourceType = _controllerResourceMapping.GetAssociatedResource(controllerName); - var requestResource = _resourceGraph.GetContextEntity(resourceType); + var requestResource = _resourceGraph.GetResourceContext(resourceType); if (requestResource == null) return requestResource; var rd = _httpContext.GetRouteData().Values; diff --git a/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs b/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs index 0465dd00b9..8ba43dbc26 100644 --- a/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs @@ -12,9 +12,9 @@ namespace JsonApiDotNetCore.Middleware /// public class DefaultTypeMatchFilter : IActionFilter { - private readonly IContextEntityProvider _provider; + private readonly IResourceContextProvider _provider; - public DefaultTypeMatchFilter(IContextEntityProvider provider) + public DefaultTypeMatchFilter(IResourceContextProvider provider) { _provider = provider; } @@ -29,11 +29,11 @@ public void OnActionExecuting(ActionExecutingContext context) if (deserializedType != null && targetType != null && deserializedType != targetType) { - var expectedJsonApiResource = _provider.GetContextEntity(targetType); + var expectedJsonApiResource = _provider.GetResourceContext(targetType); throw new JsonApiException(409, $"Cannot '{context.HttpContext.Request.Method}' type '{deserializedType.Name}' " - + $"to '{expectedJsonApiResource?.EntityName}' endpoint.", + + $"to '{expectedJsonApiResource?.ResourceName}' endpoint.", detail: "Check that the request payload type matches the type expected by this endpoint."); } } diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs index 6d60f4b660..853a414b8f 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs @@ -85,14 +85,14 @@ public override object GetValue(object entity) if (throughEntities == null) // return an empty list for the right-type of the property. - return TypeHelper.CreateListFor(DependentType); + return TypeHelper.CreateListFor(RightType); // the right entities are included on the navigation/through entities. Extract and return them. var rightEntities = new List(); foreach (var rightEntity in (IList)throughEntities) rightEntities.Add((IIdentifiable)RightProperty.GetValue(rightEntity)); - return rightEntities.Cast(DependentType); + return rightEntities.Cast(RightType); } diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs index 1037aa9882..772eb32457 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs @@ -1,4 +1,3 @@ -using System; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Models.Links; @@ -12,7 +11,7 @@ public class HasOneAttribute : RelationshipAttribute /// /// The relationship name as exposed by the API /// Enum to set which links should be outputted for this relationship. Defaults to which means that the configuration in - /// or is used. + /// or is used. /// Whether or not this relationship can be included using the ?include=public-name query string /// The foreign key property name. Defaults to "{RelationshipName}Id" /// The name of the entity mapped property, defaults to null diff --git a/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs index 1a5bd5dea2..0e92e5ea84 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs @@ -30,30 +30,15 @@ protected RelationshipAttribute(string publicName, Link relationshipLinks, bool /// /// /// - /// public List<Tag> Tags { get; sit; } // Type => Tag - /// - /// - [Obsolete("Use property DependentType")] - public Type Type { get { return DependentType; } internal set { DependentType = value; } } - - /// - /// The related entity type. This does not necessarily match the navigation property type. - /// In the case of a HasMany relationship, this value will be the generic argument type. - /// - /// The technical language as used in EF Core is used here (dependent vs principal). - /// - /// - /// - /// /// public List<Tag> Tags { get; set; } // Type => Tag /// /// - public Type DependentType { get; internal set; } + public Type RightType { get; internal set; } /// - /// The parent entity type. The technical language as used in EF Core is used here (dependent vs principal). + /// The parent entity type. This is the type of the class in which this attribute was used. /// - public Type PrincipalType { get; internal set; } + public Type LeftType { get; internal set; } public bool IsHasMany => GetType() == typeof(HasManyAttribute) || GetType().Inherits(typeof(HasManyAttribute)); public bool IsHasOne => GetType() == typeof(HasOneAttribute); @@ -83,12 +68,7 @@ public override bool Equals(object obj) } bool equalRelationshipName = PublicRelationshipName.Equals(attr.PublicRelationshipName); - bool equalPrincipalType = true; - if (PrincipalType != null) - { - equalPrincipalType = PrincipalType.Equals(attr.PrincipalType); - } - return IsHasMany == attr.IsHasMany && equalRelationshipName && equalPrincipalType; + return IsHasMany == attr.IsHasMany && equalRelationshipName; } /// @@ -104,6 +84,5 @@ public virtual bool Is(string publicRelationshipName) /// In all cases except the HasManyThrough relationships, this will just be the . /// public virtual string RelationshipPath => InternalRelationshipName; - } } diff --git a/src/JsonApiDotNetCore/Models/IHasMeta.cs b/src/JsonApiDotNetCore/Models/IHasMeta.cs index 34efffdabd..f1605bf790 100644 --- a/src/JsonApiDotNetCore/Models/IHasMeta.cs +++ b/src/JsonApiDotNetCore/Models/IHasMeta.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using JsonApiDotNetCore.Services; namespace JsonApiDotNetCore.Models { diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index d23df72e30..6f7798e484 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using JsonApiDotNetCore.Services; -using System.Collections; namespace JsonApiDotNetCore.Models { @@ -28,15 +26,15 @@ public interface IResourceDefinition /// The resource type public class ResourceDefinition : IResourceDefinition, IResourceHookContainer where TResource : class, IIdentifiable { - private readonly ContextEntity _contextEntity; + private readonly ResourceContext _resourceContext; private readonly IResourceGraph _resourceGraph; private List _allowedAttributes; private List _allowedRelationships; public ResourceDefinition(IResourceGraph resourceGraph) { - _contextEntity = resourceGraph.GetContextEntity(typeof(TResource)); - _allowedAttributes = _contextEntity.Attributes; - _allowedRelationships = _contextEntity.Relationships; + _resourceContext = resourceGraph.GetResourceContext(typeof(TResource)); + _allowedAttributes = _resourceContext.Attributes; + _allowedRelationships = _resourceContext.Relationships; _resourceGraph = resourceGraph; } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs index fa57e1850f..cadd91f6fd 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs @@ -15,7 +15,7 @@ namespace JsonApiDotNetCore.Query public abstract class QueryParameterService { protected readonly IResourceGraph _resourceGraph; - protected readonly ContextEntity _requestResource; + protected readonly ResourceContext _requestResource; protected QueryParameterService(IResourceGraph resourceGraph, ICurrentRequest currentRequest) { @@ -48,7 +48,7 @@ protected AttrAttribute GetAttribute(string target, RelationshipAttribute relati { AttrAttribute attribute; if (relationship != null) - attribute = _resourceGraph.GetAttributes(relationship.DependentType).FirstOrDefault(a => a.Is(target)); + attribute = _resourceGraph.GetAttributes(relationship.RightType).FirstOrDefault(a => a.Is(target)); else attribute = _requestResource.Attributes.FirstOrDefault(attr => attr.Is(target)); @@ -66,7 +66,7 @@ protected RelationshipAttribute GetRelationship(string propertyName) if (propertyName == null) return null; var relationship = _requestResource.Relationships.FirstOrDefault(r => r.Is(propertyName)); if (relationship == null) - throw new JsonApiException(400, $"{propertyName} is not a valid relationship on {_requestResource.EntityName}."); + throw new JsonApiException(400, $"{propertyName} is not a valid relationship on {_requestResource.ResourceName}."); return relationship; } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs index 2984139fe7..feed8c49fe 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs @@ -18,7 +18,7 @@ public class FilterService : QueryParameterService, IFilterService public FilterService(IResourceDefinitionProvider resourceDefinitionProvider, IResourceGraph resourceGraph, ICurrentRequest currentRequest) : base(resourceGraph, currentRequest) { - _requestResourceDefinition = resourceDefinitionProvider.Get(_requestResource.EntityType); + _requestResourceDefinition = resourceDefinitionProvider.Get(_requestResource.ResourceType); _filters = new List(); } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs index 816033eab1..4032da4c53 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs @@ -52,20 +52,20 @@ private void ParseChain(string chain) throw CannotIncludeError(resourceContext, relationshipName); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); + resourceContext = _resourceGraph.GetResourceContext(relationship.RightType); } _includedChains.Add(parsedChain); } - private JsonApiException CannotIncludeError(ContextEntity resourceContext, string requestedRelationship) + private JsonApiException CannotIncludeError(ResourceContext resourceContext, string requestedRelationship) { - return new JsonApiException(400, $"Including the relationship {requestedRelationship} on {resourceContext.EntityName} is not allowed"); + return new JsonApiException(400, $"Including the relationship {requestedRelationship} on {resourceContext.ResourceName} is not allowed"); } - private JsonApiException InvalidRelationshipError(ContextEntity resourceContext, string requestedRelationship) + private JsonApiException InvalidRelationshipError(ResourceContext resourceContext, string requestedRelationship) { - return new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {resourceContext.EntityName}", - $"{resourceContext.EntityName} does not have a relationship named {requestedRelationship}"); + return new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {resourceContext.ResourceName}", + $"{resourceContext.ResourceName} does not have a relationship named {requestedRelationship}"); } } } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs index 45a8fca0bb..cd5a283e07 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs @@ -39,7 +39,7 @@ public List Get() { if (_queries == null) { - var requestResourceDefinition = _resourceDefinitionProvider.Get(_requestResource.EntityType); + var requestResourceDefinition = _resourceDefinitionProvider.Get(_requestResource.ResourceType); if (requestResourceDefinition != null) return requestResourceDefinition.DefaultSort()?.Select(d => BuildQueryContext(new SortQuery(d.Item1.PublicAttributeName, d.Item2))).ToList(); } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs index e6b0fcf56b..f20542562d 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs @@ -39,7 +39,7 @@ public List Get(RelationshipAttribute relationship = null) _selectedRelationshipFields.TryGetValue(relationship, out var fields); return fields; } - + /// public virtual void Parse(KeyValuePair queryParameter) { // expected: articles?fields=prop1,prop2 @@ -61,7 +61,7 @@ public virtual void Parse(KeyValuePair queryParameter) // it is possible that the request resource has a relationship // that is equal to the resource name, like with self-referering data types (eg directory structures) // if not, no longer support this type of sparse field selection. - if (navigation == _requestResource.EntityName && !_requestResource.Relationships.Any(a => a.Is(navigation))) + if (navigation == _requestResource.ResourceName && !_requestResource.Relationships.Any(a => a.Is(navigation))) throw new JsonApiException(400, $"Use \"?fields=...\" instead of \"fields[{navigation}]\":" + $" the square bracket navigations is now reserved " + $"for relationships only. See https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/555#issuecomment-543100865"); @@ -71,7 +71,7 @@ public virtual void Parse(KeyValuePair queryParameter) var relationship = _requestResource.Relationships.SingleOrDefault(a => a.Is(navigation)); if (relationship == null) - throw new JsonApiException(400, $"\"{navigation}\" in \"fields[{navigation}]\" is not a valid relationship of {_requestResource.EntityName}"); + throw new JsonApiException(400, $"\"{navigation}\" in \"fields[{navigation}]\" is not a valid relationship of {_requestResource.ResourceName}"); foreach (var field in fields) RegisterRelatedResourceField(field, relationship); @@ -83,10 +83,10 @@ public virtual void Parse(KeyValuePair queryParameter) /// private void RegisterRelatedResourceField(string field, RelationshipAttribute relationship) { - var relationProperty = _resourceGraph.GetContextEntity(relationship.DependentType); + var relationProperty = _resourceGraph.GetResourceContext(relationship.RightType); var attr = relationProperty.Attributes.SingleOrDefault(a => a.Is(field)); if (attr == null) - throw new JsonApiException(400, $"'{relationship.DependentType.Name}' does not contain '{field}'."); + throw new JsonApiException(400, $"'{relationship.RightType.Name}' does not contain '{field}'."); if (!_selectedRelationshipFields.TryGetValue(relationship, out var registeredFields)) _selectedRelationshipFields.Add(relationship, registeredFields = new List()); @@ -100,7 +100,7 @@ private void RegisterRequestResourceField(string field) { var attr = _requestResource.Attributes.SingleOrDefault(a => a.Is(field)); if (attr == null) - throw new JsonApiException(400, $"'{_requestResource.EntityName}' does not contain '{field}'."); + throw new JsonApiException(400, $"'{_requestResource.ResourceName}' does not contain '{field}'."); (_selectedFields = _selectedFields ?? new List()).Add(attr); } diff --git a/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs b/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs index ac65ffbc2c..0260db8c91 100644 --- a/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs +++ b/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs @@ -1,11 +1,9 @@ -using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Managers.Contracts { /// - /// This is the former RequestManager. TODO: not done. /// Metadata associated to the current json:api request. /// public interface ICurrentRequest @@ -35,9 +33,9 @@ public interface ICurrentRequest /// /// Sets the current context entity for this entire request /// - /// - void SetRequestResource(ContextEntity contextEntityCurrent); + /// + void SetRequestResource(ResourceContext currentResourceContext); - ContextEntity GetRequestResource(); + ResourceContext GetRequestResource(); } } diff --git a/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs b/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs index bbaf8c037c..abb1b5863a 100644 --- a/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs +++ b/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCore.Managers { class CurrentRequest : ICurrentRequest { - private ContextEntity _contextEntity; + private ResourceContext _resourceContext; public string BasePath { get; set; } public bool IsRelationshipPath { get; set; } public RelationshipAttribute RequestRelationship { get; set; } @@ -15,14 +15,14 @@ class CurrentRequest : ICurrentRequest /// The main resource of the request. /// /// - public ContextEntity GetRequestResource() + public ResourceContext GetRequestResource() { - return _contextEntity; + return _resourceContext; } - public void SetRequestResource(ContextEntity primaryResource) + public void SetRequestResource(ResourceContext primaryResource) { - _contextEntity = primaryResource; + _resourceContext = primaryResource; } } } diff --git a/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs b/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs index 31a3c78d1d..b5446325ad 100644 --- a/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs @@ -13,7 +13,7 @@ namespace JsonApiDotNetCore.Serialization.Client /// public class ResponseDeserializer : BaseDocumentParser, IResponseDeserializer { - public ResponseDeserializer(IContextEntityProvider provider) : base(provider) { } + public ResponseDeserializer(IResourceContextProvider provider) : base(provider) { } /// public DeserializedSingleResponse DeserializeSingle(string body) where TResource : class, IIdentifiable @@ -71,7 +71,7 @@ protected override void AfterProcessField(IIdentifiable entity, IResourceField f } else if (field is HasManyAttribute hasManyAttr) { // add attributes and relationships of a parsed HasMany relationship - var values = TypeHelper.CreateListFor(hasManyAttr.DependentType); + var values = TypeHelper.CreateListFor(hasManyAttr.RightType); foreach (var rio in data.ManyData) values.Add(ParseIncludedRelationship(hasManyAttr, rio)); @@ -84,19 +84,19 @@ protected override void AfterProcessField(IIdentifiable entity, IResourceField f /// private IIdentifiable ParseIncludedRelationship(RelationshipAttribute relationshipAttr, ResourceIdentifierObject relatedResourceIdentifier) { - var relatedInstance = relationshipAttr.DependentType.New(); + var relatedInstance = relationshipAttr.RightType.New(); relatedInstance.StringId = relatedResourceIdentifier.Id; var includedResource = GetLinkedResource(relatedResourceIdentifier); if (includedResource == null) return relatedInstance; - var contextEntity = _provider.GetContextEntity(relatedResourceIdentifier.Type); - if (contextEntity == null) - throw new InvalidOperationException($"Included type '{relationshipAttr.DependentType}' is not a registered json:api resource."); + var resourceContext = _provider.GetResourceContext(relatedResourceIdentifier.Type); + if (resourceContext == null) + throw new InvalidOperationException($"Included type '{relationshipAttr.RightType}' is not a registered json:api resource."); - SetAttributes(relatedInstance, includedResource.Attributes, contextEntity.Attributes); - SetRelationships(relatedInstance, includedResource.Relationships, contextEntity.Relationships); + SetAttributes(relatedInstance, includedResource.Attributes, resourceContext.Attributes); + SetRelationships(relatedInstance, includedResource.Relationships, resourceContext.Relationships); return relatedInstance; } diff --git a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs index 845a711146..ede8418eed 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs @@ -18,10 +18,10 @@ namespace JsonApiDotNetCore.Serialization /// public abstract class BaseDocumentParser { - protected readonly IContextEntityProvider _provider; + protected readonly IResourceContextProvider _provider; protected Document _document; - protected BaseDocumentParser(IContextEntityProvider provider) + protected BaseDocumentParser(IResourceContextProvider provider) { _provider = provider; } @@ -128,8 +128,8 @@ private JToken LoadJToken(string body) /// The parsed entity private IIdentifiable ParseResourceObject(ResourceObject data) { - var contextEntity = _provider.GetContextEntity(data.Type); - if (contextEntity == null) + var resourceContext = _provider.GetResourceContext(data.Type); + if (resourceContext == null) { throw new JsonApiException(400, message: $"This API does not contain a json:api resource named '{data.Type}'.", @@ -138,10 +138,10 @@ private IIdentifiable ParseResourceObject(ResourceObject data) + "If you have manually registered the resource, check that the call to AddResource correctly sets the public name."); } - var entity = (IIdentifiable)Activator.CreateInstance(contextEntity.EntityType); + var entity = (IIdentifiable)Activator.CreateInstance(resourceContext.ResourceType); - entity = SetAttributes(entity, data.Attributes, contextEntity.Attributes); - entity = SetRelationships(entity, data.Relationships, contextEntity.Relationships); + entity = SetAttributes(entity, data.Attributes, resourceContext.Attributes); + entity = SetRelationships(entity, data.Relationships, resourceContext.Relationships); if (data.Id != null) entity.StringId = data.Id?.ToString(); @@ -213,7 +213,7 @@ private void SetNavigation(IIdentifiable entity, HasOneAttribute attr, string re } else { - var relatedInstance = attr.DependentType.New(); + var relatedInstance = attr.RightType.New(); relatedInstance.StringId = relatedId; attr.SetValue(entity, relatedInstance); } @@ -230,11 +230,11 @@ private object SetHasManyRelationship(IIdentifiable entity, { // if the relationship is set to null, no need to set the navigation property to null: this is the default value. var relatedResources = relationshipData.ManyData.Select(rio => { - var relatedInstance = attr.DependentType.New(); + var relatedInstance = attr.RightType.New(); relatedInstance.StringId = rio.Id; return relatedInstance; }); - var convertedCollection = TypeHelper.ConvertCollection(relatedResources, attr.DependentType); + var convertedCollection = TypeHelper.ConvertCollection(relatedResources, attr.RightType); attr.SetValue(entity, convertedCollection); } diff --git a/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs b/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs index 4fab07117a..54e4066ecf 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs @@ -11,9 +11,9 @@ namespace JsonApiDotNetCore.Serialization /// public abstract class BaseDocumentBuilder { - protected readonly IContextEntityProvider _provider; + protected readonly IResourceContextProvider _provider; protected readonly IResourceObjectBuilder _resourceObjectBuilder; - protected BaseDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder, IContextEntityProvider provider) + protected BaseDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder, IResourceContextProvider provider) { _resourceObjectBuilder = resourceObjectBuilder; _provider = provider; diff --git a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs index 589d4ef4ac..6e0dde1fb0 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs @@ -12,11 +12,11 @@ namespace JsonApiDotNetCore.Serialization /// public class ResourceObjectBuilder : IResourceObjectBuilder { - protected readonly IContextEntityProvider _provider; + protected readonly IResourceContextProvider _provider; private readonly ResourceObjectBuilderSettings _settings; private const string _identifiablePropertyName = nameof(Identifiable.Id); - public ResourceObjectBuilder(IContextEntityProvider provider, ResourceObjectBuilderSettings settings) + public ResourceObjectBuilder(IResourceContextProvider provider, ResourceObjectBuilderSettings settings) { _provider = provider; _settings = settings; @@ -25,10 +25,10 @@ public ResourceObjectBuilder(IContextEntityProvider provider, ResourceObjectBuil /// public ResourceObject Build(IIdentifiable entity, IEnumerable attributes = null, IEnumerable relationships = null) { - var resourceContext = _provider.GetContextEntity(entity.GetType()); + var resourceContext = _provider.GetResourceContext(entity.GetType()); // populating the top-level "type" and "id" members. - var ro = new ResourceObject { Type = resourceContext.EntityName, Id = entity.StringId.NullIfEmpty() }; + var ro = new ResourceObject { Type = resourceContext.ResourceName, Id = entity.StringId.NullIfEmpty() }; // populating the top-level "attribute" member of a resource object. never include "id" as an attribute if (attributes != null && (attributes = attributes.Where(attr => attr.InternalAttributeName != _identifiablePropertyName)).Any()) @@ -98,7 +98,7 @@ private List GetRelatedResourceLinkage(HasManyAttribut /// private ResourceIdentifierObject GetResourceIdentifier(IIdentifiable entity) { - var resourceName = _provider.GetContextEntity(entity.GetType()).EntityName; + var resourceName = _provider.GetResourceContext(entity.GetType()).ResourceName; return new ResourceIdentifierObject { Type = resourceName, diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs index c663b8ec5a..4ce2aa5f94 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs @@ -16,7 +16,7 @@ public class IncludedResourceObjectBuilder : ResourceObjectBuilder, IIncludedRes public IncludedResourceObjectBuilder(IFieldsToSerialize fieldsToSerialize, ILinkBuilder linkBuilder, - IContextEntityProvider provider, + IResourceContextProvider provider, IResourceObjectBuilderSettingsProvider settingsProvider) : base(provider, settingsProvider.Get()) { @@ -121,7 +121,7 @@ protected override RelationshipEntry GetRelationshipData(RelationshipAttribute r private ResourceObject GetOrBuildResourceObject(IIdentifiable parent, RelationshipAttribute relationship) { var type = parent.GetType(); - var resourceName = _provider.GetContextEntity(type).EntityName; + var resourceName = _provider.GetResourceContext(type).ResourceName; var entry = _included.SingleOrDefault(ro => ro.Type == resourceName && ro.Id == parent.StringId); if (entry == null) { diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs index fe2badc807..9b00cdc23a 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCore.Serialization.Server.Builders { public class LinkBuilder : ILinkBuilder { - private readonly IContextEntityProvider _provider; + private readonly IResourceContextProvider _provider; private readonly ILinksConfiguration _options; private readonly ICurrentRequest _currentRequest; private readonly IPageService _pageService; @@ -18,7 +18,7 @@ public class LinkBuilder : ILinkBuilder public LinkBuilder(ILinksConfiguration options, ICurrentRequest currentRequest, IPageService pageService, - IContextEntityProvider provider) + IResourceContextProvider provider) { _options = options; _currentRequest = currentRequest; @@ -27,11 +27,11 @@ public LinkBuilder(ILinksConfiguration options, } /// - public TopLevelLinks GetTopLevelLinks(ContextEntity primaryResource) + public TopLevelLinks GetTopLevelLinks(ResourceContext primaryResource) { TopLevelLinks topLevelLinks = null; if (ShouldAddTopLevelLink(primaryResource, Link.Self)) - topLevelLinks = new TopLevelLinks { Self = GetSelfTopLevelLink(primaryResource.EntityName) }; + topLevelLinks = new TopLevelLinks { Self = GetSelfTopLevelLink(primaryResource.ResourceName) }; if (ShouldAddTopLevelLink(primaryResource, Link.Paging)) SetPageLinks(primaryResource, ref topLevelLinks); @@ -41,18 +41,18 @@ public TopLevelLinks GetTopLevelLinks(ContextEntity primaryResource) /// /// Checks if the top-level should be added by first checking - /// configuration on the , and if not configured, by checking with the + /// configuration on the , and if not configured, by checking with the /// global configuration in . /// /// - private bool ShouldAddTopLevelLink(ContextEntity primaryResource, Link link) + private bool ShouldAddTopLevelLink(ResourceContext primaryResource, Link link) { if (primaryResource.TopLevelLinks != Link.NotConfigured) return primaryResource.TopLevelLinks.HasFlag(link); return _options.TopLevelLinks.HasFlag(link); } - private void SetPageLinks(ContextEntity primaryResource, ref TopLevelLinks links) + private void SetPageLinks(ResourceContext primaryResource, ref TopLevelLinks links) { if (!_pageService.ShouldPaginate()) return; @@ -78,16 +78,16 @@ private string GetSelfTopLevelLink(string resourceName) return $"{GetBasePath()}/{resourceName}"; } - private string GetPageLink(ContextEntity primaryResource, int pageOffset, int pageSize) + private string GetPageLink(ResourceContext primaryResource, int pageOffset, int pageSize) { - return $"{GetBasePath()}/{primaryResource.EntityName}?page[size]={pageSize}&page[number]={pageOffset}"; + return $"{GetBasePath()}/{primaryResource.ResourceName}?page[size]={pageSize}&page[number]={pageOffset}"; } /// public ResourceLinks GetResourceLinks(string resourceName, string id) { - var resourceContext = _provider.GetContextEntity(resourceName); + var resourceContext = _provider.GetResourceContext(resourceName); if (ShouldAddResourceLink(resourceContext, Link.Self)) return new ResourceLinks { Self = GetSelfResourceLink(resourceName, id) }; @@ -97,16 +97,16 @@ public ResourceLinks GetResourceLinks(string resourceName, string id) /// public RelationshipLinks GetRelationshipLinks(RelationshipAttribute relationship, IIdentifiable parent) { - var parentResourceContext = _provider.GetContextEntity(parent.GetType()); + var parentResourceContext = _provider.GetResourceContext(parent.GetType()); var childNavigation = relationship.PublicRelationshipName; RelationshipLinks links = null; if (ShouldAddRelationshipLink(parentResourceContext, relationship, Link.Related)) - links = new RelationshipLinks { Related = GetRelatedRelationshipLink(parentResourceContext.EntityName, parent.StringId, childNavigation) }; + links = new RelationshipLinks { Related = GetRelatedRelationshipLink(parentResourceContext.ResourceName, parent.StringId, childNavigation) }; if (ShouldAddRelationshipLink(parentResourceContext, relationship, Link.Self)) { links = links ?? new RelationshipLinks(); - links.Self = GetSelfRelationshipLink(parentResourceContext.EntityName, parent.StringId, childNavigation); + links.Self = GetSelfRelationshipLink(parentResourceContext.ResourceName, parent.StringId, childNavigation); } return links; @@ -130,11 +130,11 @@ private string GetRelatedRelationshipLink(string parent, string parentId, string /// /// Checks if the resource object level should be added by first checking - /// configuration on the , and if not configured, by checking with the + /// configuration on the , and if not configured, by checking with the /// global configuration in . /// /// - private bool ShouldAddResourceLink(ContextEntity resourceContext, Link link) + private bool ShouldAddResourceLink(ResourceContext resourceContext, Link link) { if (resourceContext.ResourceLinks != Link.NotConfigured) return resourceContext.ResourceLinks.HasFlag(link); @@ -144,11 +144,11 @@ private bool ShouldAddResourceLink(ContextEntity resourceContext, Link link) /// /// Checks if the resource object level should be added by first checking /// configuration on the attribute, if not configured by checking - /// the , and if not configured by checking with the + /// the , and if not configured by checking with the /// global configuration in . /// /// - private bool ShouldAddRelationshipLink(ContextEntity resourceContext, RelationshipAttribute relationship, Link link) + private bool ShouldAddRelationshipLink(ResourceContext resourceContext, RelationshipAttribute relationship, Link link) { if (relationship.RelationshipLinks != Link.NotConfigured) return relationship.RelationshipLinks.HasFlag(link); diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs index c8e134d001..fadc819581 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs @@ -17,7 +17,7 @@ public class ResponseResourceObjectBuilder : ResourceObjectBuilder, IResourceObj public ResponseResourceObjectBuilder(ILinkBuilder linkBuilder, IIncludedResourceObjectBuilder includedBuilder, IIncludeService includeService, - IContextEntityProvider provider, + IResourceContextProvider provider, IResourceObjectBuilderSettingsProvider settingsProvider) : base(provider, settingsProvider.Get()) { diff --git a/src/JsonApiDotNetCore/Serialization/Server/Contracts/ILinkBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Contracts/ILinkBuilder.cs index 4a6ae113bf..a4bd87195a 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Contracts/ILinkBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Contracts/ILinkBuilder.cs @@ -13,7 +13,7 @@ public interface ILinkBuilder /// Builds the links object that is included in the top-level of the document. /// /// The primary resource of the response body - TopLevelLinks GetTopLevelLinks(ContextEntity primaryResource); + TopLevelLinks GetTopLevelLinks(ResourceContext primaryResource); /// /// Builds the links object for resources in the primary data. /// diff --git a/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs b/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs index 43dbcd6417..4bdf99654a 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs @@ -1,7 +1,6 @@ using JsonApiDotNetCore.Internal.Contracts; using System; using System.Collections.Generic; -using JsonApiDotNetCore.Services; using JsonApiDotNetCore.Query; using System.Linq; using JsonApiDotNetCore.Models; diff --git a/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs index 7283707b42..441f873bd1 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs @@ -11,7 +11,7 @@ public class RequestDeserializer : BaseDocumentParser, IJsonApiDeserializer { private readonly ITargetedFields _targetedFields; - public RequestDeserializer(IContextEntityProvider provider, + public RequestDeserializer(IResourceContextProvider provider, ITargetedFields targetedFields) : base(provider) { _targetedFields = targetedFields; diff --git a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs index 62867eae57..dd138d3697 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs @@ -41,7 +41,7 @@ public ResponseSerializer(IMetaBuilder metaBuilder, IIncludedResourceObjectBuilder includedBuilder, IFieldsToSerialize fieldsToSerialize, IResourceObjectBuilder resourceObjectBuilder, - IContextEntityProvider provider) : + IResourceContextProvider provider) : base(resourceObjectBuilder, provider) { _fieldsToSerialize = fieldsToSerialize; @@ -158,7 +158,7 @@ private List GetRelationshipsToSerialize(Type resourceTyp /// private void AddTopLevelObjects(Document document) { - document.Links = _linkBuilder.GetTopLevelLinks(_provider.GetContextEntity()); + document.Links = _linkBuilder.GetTopLevelLinks(_provider.GetResourceContext()); document.Meta = _metaBuilder.GetMeta(); document.Included = _includedBuilder.Build(); } diff --git a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs index 7554e27fe8..9b39778d45 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs @@ -1,10 +1,7 @@ using System; -using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Managers.Contracts; -using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; -using Microsoft.Extensions.DependencyInjection; namespace JsonApiDotNetCore.Serialization.Server { @@ -24,7 +21,7 @@ public ResponseSerializerFactory(ICurrentRequest currentRequest, IScopedServiceP } /// - /// Initializes the server serializer using the + /// Initializes the server serializer using the /// associated with the current request. /// public IJsonApiSerializer GetSerializer() @@ -44,9 +41,9 @@ public IJsonApiSerializer GetSerializer() private Type GetDocumentPrimaryType() { if (_currentRequest.RequestRelationship != null && !_currentRequest.IsRelationshipPath) - return _currentRequest.RequestRelationship.DependentType; + return _currentRequest.RequestRelationship.RightType; - return _currentRequest.GetRequestResource()?.EntityType; + return _currentRequest.GetRequestResource()?.ResourceType; } } } diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs similarity index 92% rename from src/JsonApiDotNetCore/Services/EntityResourceService.cs rename to src/JsonApiDotNetCore/Services/DefaultResourceService.cs index 58d7ad1b3d..5c79f0dc5c 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs @@ -18,7 +18,7 @@ namespace JsonApiDotNetCore.Services /// /// /// - public class EntityResourceService : + public class DefaultResourceService : IResourceService where TResource : class, IIdentifiable { @@ -26,22 +26,22 @@ public class EntityResourceService : private readonly IJsonApiOptions _options; private readonly IFilterService _filterService; private readonly ISortService _sortService; - private readonly IEntityRepository _repository; + private readonly IResourceRepository _repository; private readonly ILogger _logger; private readonly IResourceHookExecutor _hookExecutor; private readonly IIncludeService _includeService; private readonly ISparseFieldsService _sparseFieldsService; - private readonly ContextEntity _currentRequestResource; + private readonly ResourceContext _currentRequestResource; - public EntityResourceService( + public DefaultResourceService( ISortService sortService, IFilterService filterService, - IEntityRepository repository, IJsonApiOptions options, IIncludeService includeService, ISparseFieldsService sparseFieldsService, IPageService pageManager, - IContextEntityProvider provider, + IResourceRepository repository, + IResourceContextProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) { @@ -53,8 +53,8 @@ public EntityResourceService( _filterService = filterService; _repository = repository; _hookExecutor = hookExecutor; - _logger = loggerFactory?.CreateLogger>(); - _currentRequestResource = provider.GetContextEntity(); + _logger = loggerFactory?.CreateLogger>(); + _currentRequestResource = provider.GetResourceContext(); } public virtual async Task CreateAsync(TResource entity) @@ -263,7 +263,7 @@ protected virtual IQueryable ApplySelect(IQueryable entiti { var fields = _sparseFieldsService.Get(); if (fields != null && fields.Any()) - entities = _repository.Select(entities, fields); + entities = _repository.Select(entities, fields.ToArray()); return entities; } @@ -276,14 +276,14 @@ protected virtual IQueryable ApplySelect(IQueryable entiti private async Task GetWithRelationshipsAsync(TId id) { var sparseFieldset = _sparseFieldsService.Get(); - var query = _repository.Select(_repository.Get(id), sparseFieldset); + var query = _repository.Select(_repository.Get(id), sparseFieldset.ToArray()); foreach (var chain in _includeService.Get()) query = _repository.Include(query, chain.ToArray()); TResource value; // https://github.com/aspnet/EntityFrameworkCore/issues/6573 - if (sparseFieldset.Count() > 0) + if (sparseFieldset.Any()) value = query.FirstOrDefault(); else value = await _repository.FirstOrDefaultAsync(query); @@ -319,15 +319,15 @@ private List AsList(TResource entity) /// No mapping with integer as default /// /// - public class EntityResourceService : EntityResourceService, + public class DefaultResourceService : DefaultResourceService, IResourceService where TResource : class, IIdentifiable { - public EntityResourceService(ISortService sortService, IFilterService filterService, IEntityRepository repository, + public DefaultResourceService(ISortService sortService, IFilterService filterService, IResourceRepository repository, IJsonApiOptions options, IIncludeService includeService, ISparseFieldsService sparseFieldsService, - IPageService pageManager, IContextEntityProvider provider, + IPageService pageManager, IResourceContextProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) - : base(sortService, filterService, repository, options, includeService, sparseFieldsService, pageManager, provider, hookExecutor, loggerFactory) + : base(sortService, filterService, options, includeService, sparseFieldsService, pageManager, repository, provider, hookExecutor, loggerFactory) { } } diff --git a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs index 32ddcaf37c..5a9e4b655c 100644 --- a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs +++ b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs @@ -20,7 +20,7 @@ public ResourceDefinitionProvider(IResourceGraph resourceContextProvider, IScope /// public IResourceDefinition Get(Type resourceType) { - return (IResourceDefinition)_serviceProvider.GetService(_resourceContextProvider.GetContextEntity(resourceType).ResourceType); + return (IResourceDefinition)_serviceProvider.GetService(_resourceContextProvider.GetResourceContext(resourceType).ResourceDefinitionType); } } } diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index c4cec73a15..f7cbb834bd 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -5,9 +5,7 @@ using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Hooks; -using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Contracts; -using JsonApiDotNetCore.Managers.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.EntityFrameworkCore; @@ -41,9 +39,9 @@ public void AddAssembly_Adds_All_Resources_To_Graph() // assert var resourceGraph = _resourceGraphBuilder.Build(); - var personResource = resourceGraph.GetContextEntity(typeof(Person)); - var articleResource = resourceGraph.GetContextEntity(typeof(Article)); - var modelResource = resourceGraph.GetContextEntity(typeof(Model)); + var personResource = resourceGraph.GetResourceContext(typeof(Person)); + var articleResource = resourceGraph.GetResourceContext(typeof(Article)); + var modelResource = resourceGraph.GetResourceContext(typeof(Model)); Assert.NotNull(personResource); Assert.NotNull(articleResource); @@ -58,7 +56,7 @@ public void AddCurrentAssembly_Adds_Resources_To_Graph() // assert var resourceGraph = _resourceGraphBuilder.Build(); - var testModelResource = resourceGraph.GetContextEntity(typeof(TestModel)); + var testModelResource = resourceGraph.GetResourceContext(typeof(TestModel)); Assert.NotNull(testModelResource); } @@ -88,18 +86,18 @@ public void AddCurrentAssembly_Adds_Repositories_To_Container() // assert var services = _services.BuildServiceProvider(); - Assert.IsType(services.GetService>()); + Assert.IsType(services.GetService>()); } public class TestModel : Identifiable { } - public class TestModelService : EntityResourceService + public class TestModelService : DefaultResourceService { - private static IEntityRepository _repo = new Mock>().Object; + private static IResourceRepository _repo = new Mock>().Object; private static IJsonApiContext _jsonApiContext = new Mock().Object; public TestModelService( - IEntityRepository repository, + IResourceRepository repository, IJsonApiOptions options, IRequestContext currentRequest, IPageQueryService pageService, @@ -110,7 +108,7 @@ public TestModelService( } } - public class TestModelRepository : DefaultEntityRepository + public class TestModelRepository : DefaultResourceRepository { internal static IDbContextResolver _dbContextResolver; private static IJsonApiContext _jsonApiContext = new Mock().Object; diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs index 67ec9b53ba..16c83ad13a 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs @@ -73,7 +73,7 @@ public async Task Can_Select_Sparse_Fieldsets() var query = _dbContext .TodoItems .Where(t => t.Id == todoItem.Id) - .Select(_resourceGraph.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } ).ToList()); + .Select(_resourceGraph.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } ).ToArray()); var resultSql = StringExtensions.Normalize(query.ToSql()); var result = await query.FirstAsync(); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs index a0def3595c..f4db2a89b1 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs @@ -18,7 +18,6 @@ public class TestFixture : IDisposable where TStartup : class { private readonly TestServer _server; private IServiceProvider _services; - public TestFixture() { var builder = new WebHostBuilder() diff --git a/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/IQueryableExtensions.cs b/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/IQueryableExtensions.cs index 9298d93a05..f9558e9e86 100644 --- a/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/IQueryableExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/IQueryableExtensions.cs @@ -21,10 +21,10 @@ public static class IQueryableExtensions private static readonly PropertyInfo DependenciesProperty = typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies"); - public static string ToSql(this IQueryable queryable) - where TEntity : class + public static string ToSql(this IQueryable queryable) + where TResource : class { - if (!(queryable is EntityQueryable) && !(queryable is InternalDbSet)) + if (!(queryable is EntityQueryable) && !(queryable is InternalDbSet)) throw new ArgumentException(); var queryCompiler = (IQueryCompiler)QueryCompilerField.GetValue(queryable.Provider); @@ -34,7 +34,7 @@ public static string ToSql(this IQueryable queryable) var queryCompilationContextFactory = ((DatabaseDependencies)DependenciesProperty.GetValue(database)).QueryCompilationContextFactory; var queryCompilationContext = queryCompilationContextFactory.Create(false); var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor(); - modelVisitor.CreateQueryExecutor(queryModel); + modelVisitor.CreateQueryExecutor(queryModel); return modelVisitor.Queries.Join(Environment.NewLine + Environment.NewLine); } } diff --git a/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs b/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs index e964da8d1e..4ff89428f2 100644 --- a/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs +++ b/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs @@ -35,8 +35,7 @@ public async Task Can_Get_TodoItems() // act var response = await client.SendAsync(request); var responseBody = await response.Content.ReadAsStringAsync(); - var deserializedBody = _fixture.Server.GetDeserializer() - .DeserializeList(responseBody); + var deserializedBody = _fixture.GetDeserializer().DeserializeList(responseBody).Data; // assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -62,8 +61,7 @@ public async Task Can_Get_TodoItems_By_Id() // act var response = await client.SendAsync(request); var responseBody = await response.Content.ReadAsStringAsync(); - var deserializedBody = (TodoItem)_fixture.Server.GetDeserializer() - .Deserialize(responseBody); + var deserializedBody = _fixture.GetDeserializer().DeserializeSingle(responseBody).Data; // assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -99,8 +97,7 @@ public async Task Can_Create_TodoItems() // act var response = await client.SendAsync(request); var responseBody = await response.Content.ReadAsStringAsync(); - var deserializedBody = (TodoItem)_fixture.Server.GetDeserializer() - .Deserialize(responseBody); + var deserializedBody = _fixture.GetDeserializer().DeserializeSingle(responseBody).Data; // assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); diff --git a/test/NoEntityFrameworkTests/TestFixture.cs b/test/NoEntityFrameworkTests/TestFixture.cs index 3a317e03cd..4903306613 100644 --- a/test/NoEntityFrameworkTests/TestFixture.cs +++ b/test/NoEntityFrameworkTests/TestFixture.cs @@ -1,8 +1,14 @@ +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Serialization.Client; using JsonApiDotNetCoreExample.Data; +using JsonApiDotNetCoreExample.Models; using JsonApiDotNetCoreExampleTests.Helpers.Extensions; +using JsonApiDotNetCoreExampleTests.Helpers.Models; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using System; +using System.Linq.Expressions; namespace NoEntityFrameworkTests { @@ -10,14 +16,43 @@ public class TestFixture : IDisposable { public AppDbContext Context { get; private set; } public TestServer Server { get; private set; } - + private IServiceProvider _services; public TestFixture() { var builder = new WebHostBuilder().UseStartup(); Server = new TestServer(builder); Context = Server.GetService(); Context.Database.EnsureCreated(); + _services = Server.Host.Services; + } + + public IRequestSerializer GetSerializer(Expression> attributes = null, Expression> relationships = null) where TResource : class, IIdentifiable + { + var serializer = GetService(); + if (attributes != null) + serializer.SetAttributesToSerialize(attributes); + if (relationships != null) + serializer.SetRelationshipsToSerialize(relationships); + return serializer; } + public IResponseDeserializer GetDeserializer() + { + var resourceGraph = new ResourceGraphBuilder() + .AddResource() + .AddResource
() + .AddResource() + .AddResource() + .AddResource() + .AddResource() + .AddResource() + .AddResource() + .AddResource("todo-items") + .AddResource().Build(); + return new ResponseDeserializer(resourceGraph); + } + + public T GetService() => (T)_services.GetService(typeof(T)); + public void Dispose() { diff --git a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs index a2904bc8b7..f066b20fd8 100644 --- a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs @@ -35,11 +35,11 @@ public void Can_Build_ResourceGraph_Using_Builder() // assert var resourceGraph = container.GetRequiredService(); - var dbResource = resourceGraph.GetContextEntity("db-resources"); - var nonDbResource = resourceGraph.GetContextEntity("non-db-resources"); - Assert.Equal(typeof(DbResource), dbResource.EntityType); - Assert.Equal(typeof(NonDbResource), nonDbResource.EntityType); - Assert.Equal(typeof(ResourceDefinition), nonDbResource.ResourceType); + var dbResource = resourceGraph.GetResourceContext("db-resources"); + var nonDbResource = resourceGraph.GetResourceContext("non-db-resources"); + Assert.Equal(typeof(DbResource), dbResource.ResourceType); + Assert.Equal(typeof(NonDbResource), nonDbResource.ResourceType); + Assert.Equal(typeof(ResourceDefinition), nonDbResource.ResourceDefinitionType); } [Fact] @@ -53,8 +53,8 @@ public void Resources_Without_Names_Specified_Will_Use_Default_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); - Assert.Equal("test-resources", resource.EntityName); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); + Assert.Equal("test-resources", resource.ResourceName); } [Fact] @@ -68,8 +68,8 @@ public void Resources_Without_Names_Specified_Will_Use_Configured_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); - Assert.Equal("testResources", resource.EntityName); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); + Assert.Equal("testResources", resource.ResourceName); } [Fact] @@ -83,7 +83,7 @@ public void Attrs_Without_Names_Specified_Will_Use_Default_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); Assert.Contains(resource.Attributes, (i) => i.PublicAttributeName == "compound-attribute"); } @@ -98,7 +98,7 @@ public void Attrs_Without_Names_Specified_Will_Use_Configured_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); Assert.Contains(resource.Attributes, (i) => i.PublicAttributeName == "compoundAttribute"); } @@ -113,7 +113,7 @@ public void Relationships_Without_Names_Specified_Will_Use_Default_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); Assert.Equal("related-resource", resource.Relationships.Single(r => r.IsHasOne).PublicRelationshipName); Assert.Equal("related-resources", resource.Relationships.Single(r => r.IsHasMany).PublicRelationshipName); } diff --git a/test/UnitTests/Builders/LinkBuilderTests.cs b/test/UnitTests/Builders/LinkBuilderTests.cs index 661ea73ba7..b7c5f1a3fe 100644 --- a/test/UnitTests/Builders/LinkBuilderTests.cs +++ b/test/UnitTests/Builders/LinkBuilderTests.cs @@ -42,8 +42,8 @@ public void BuildResourceLinks_GlobalAndResourceConfiguration_ExpectedResult(Lin { // arrange var config = GetConfiguration(resourceLinks: global); - var primaryResource = GetContextEntity
(resourceLinks: resource); - _provider.Setup(m => m.GetContextEntity("articles")).Returns(primaryResource); + var primaryResource = GetResourceContext
(resourceLinks: resource); + _provider.Setup(m => m.GetResourceContext("articles")).Returns(primaryResource); var builder = new LinkBuilder(config, GetRequestManager(), null, _provider.Object); // act @@ -90,10 +90,10 @@ public void BuildRelationshipLinks_GlobalResourceAndAttrConfiguration_ExpectedLi { // arrange var config = GetConfiguration(relationshipLinks: global); - var primaryResource = GetContextEntity
(relationshipLinks: resource); - _provider.Setup(m => m.GetContextEntity(typeof(Article))).Returns(primaryResource); + var primaryResource = GetResourceContext
(relationshipLinks: resource); + _provider.Setup(m => m.GetResourceContext(typeof(Article))).Returns(primaryResource); var builder = new LinkBuilder(config, GetRequestManager(), null, _provider.Object); - var attr = new HasOneAttribute(links: relationship) { DependentType = typeof(Author), PublicRelationshipName = "author" }; + var attr = new HasOneAttribute(links: relationship) { RightType = typeof(Author), PublicRelationshipName = "author" }; // act var links = builder.GetRelationshipLinks(attr, new Article { Id = 123 }); @@ -138,8 +138,8 @@ public void BuildTopLevelLinks_GlobalAndResourceConfiguration_ExpectedLinks(Link { // arrange var config = GetConfiguration(topLevelLinks: global); - var primaryResource = GetContextEntity
(topLevelLinks: resource); - _provider.Setup(m => m.GetContextEntity
()).Returns(primaryResource); + var primaryResource = GetResourceContext
(topLevelLinks: resource); + _provider.Setup(m => m.GetResourceContext
()).Returns(primaryResource); var builder = new LinkBuilder(config, GetRequestManager(), _pageService, _provider.Object); @@ -170,7 +170,7 @@ private bool CheckPages(TopLevelLinks links, bool pages) return links.First == null && links.Prev == null && links.Next == null && links.Last == null; } - private ICurrentRequest GetRequestManager(ContextEntity resourceContext = null) + private ICurrentRequest GetRequestManager(ResourceContext resourceContext = null) { var mock = new Mock(); mock.Setup(m => m.BasePath).Returns(_host); @@ -202,16 +202,16 @@ private IPageService GetPageManager() - private ContextEntity GetContextEntity(Link resourceLinks = Link.NotConfigured, + private ResourceContext GetResourceContext(Link resourceLinks = Link.NotConfigured, Link topLevelLinks = Link.NotConfigured, Link relationshipLinks = Link.NotConfigured) where TResource : class, IIdentifiable { - return new ContextEntity + return new ResourceContext { ResourceLinks = resourceLinks, TopLevelLinks = topLevelLinks, RelationshipLinks = relationshipLinks, - EntityName = typeof(TResource).Name.Dasherize() + "s" + ResourceName = typeof(TResource).Name.Dasherize() + "s" }; } } diff --git a/test/UnitTests/Data/DefaultEntityRepository_Tests.cs b/test/UnitTests/Data/DefaultEntityRepository_Tests.cs index 6c0ce780c1..8b2410dbbb 100644 --- a/test/UnitTests/Data/DefaultEntityRepository_Tests.cs +++ b/test/UnitTests/Data/DefaultEntityRepository_Tests.cs @@ -16,7 +16,7 @@ namespace UnitTests.Data { - public class DefaultEntityRepository_Tests : JsonApiControllerMixin + public class DefaultResourceRepository_Tests : JsonApiControllerMixin { private readonly Mock _currentRequestMock; private readonly Mock> _dbSetMock; @@ -25,7 +25,7 @@ public class DefaultEntityRepository_Tests : JsonApiControllerMixin private readonly Mock _contextResolverMock; private readonly TodoItem _todoItem; - public DefaultEntityRepository_Tests() + public DefaultResourceRepository_Tests() { _todoItem = new TodoItem { @@ -66,7 +66,7 @@ public async Task UpdateAsync_Updates_Attributes_In_AttributesToUpdate() Assert.Equal(todoItemUpdates.Description, updatedItem.Description); } - private DefaultEntityRepository GetRepository() + private DefaultResourceRepository GetRepository() { _contextMock @@ -80,7 +80,7 @@ private DefaultEntityRepository GetRepository() var resourceGraph = new ResourceGraphBuilder().AddResource().Build(); - return new DefaultEntityRepository( + return new DefaultResourceRepository( _targetedFieldsMock.Object, _contextResolverMock.Object, resourceGraph, null, null); diff --git a/test/UnitTests/DbSetMock.cs b/test/UnitTests/DbSetMock.cs index f56a3b1f01..fc87757815 100644 --- a/test/UnitTests/DbSetMock.cs +++ b/test/UnitTests/DbSetMock.cs @@ -32,7 +32,7 @@ public static Mock> AsDbSetMock(this List list) where T : class } } -internal class TestAsyncQueryProvider : IAsyncQueryProvider +internal class TestAsyncQueryProvider : IAsyncQueryProvider { private readonly IQueryProvider _inner; @@ -43,7 +43,7 @@ internal TestAsyncQueryProvider(IQueryProvider inner) public IQueryable CreateQuery(Expression expression) { - return new TestAsyncEnumerable(expression); + return new TestAsyncEnumerable(expression); } public IQueryable CreateQuery(Expression expression) diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index 0f2ce10f96..bdcdb002d0 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -44,10 +44,10 @@ public void AddJsonApiInternals_Adds_All_Required_Services() Assert.NotNull(currentRequest); var resourceGraph = provider.GetService(); Assert.NotNull(resourceGraph); - currentRequest.SetRequestResource(resourceGraph.GetContextEntity()); + currentRequest.SetRequestResource(resourceGraph.GetResourceContext()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); - Assert.NotNull(provider.GetService(typeof(IEntityRepository))); + Assert.NotNull(provider.GetService(typeof(IResourceRepository))); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService>()); @@ -55,8 +55,8 @@ public void AddJsonApiInternals_Adds_All_Required_Services() Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); - Assert.NotNull(provider.GetService()); - Assert.NotNull(provider.GetService(typeof(GenericProcessor))); + Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService(typeof(HasManyThroughUpdateHelper))); } [Fact] @@ -129,8 +129,8 @@ public void AddJsonApi_With_Context_Uses_DbSet_PropertyName_If_NoOtherSpecified( // assert var provider = services.BuildServiceProvider(); var resourceGraph = provider.GetService(); - var resource = resourceGraph.GetContextEntity(typeof(IntResource)); - Assert.Equal("resource", resource.EntityName); + var resource = resourceGraph.GetResourceContext(typeof(IntResource)); + Assert.Equal("resource", resource.ResourceName); } public class IntResource : Identifiable { } diff --git a/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs b/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs index d2f35dd454..bb61a42da7 100644 --- a/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs @@ -19,7 +19,7 @@ public void AddDbContext_Does_Not_Throw_If_Context_Contains_Members_That_DoNot_I var resourceGraph = resourceGraphBuilder.Build() as ResourceGraph; // assert - Assert.Empty(resourceGraph.GetContextEntities()); + Assert.Empty(resourceGraph.GetResourceContexts()); } [Fact] diff --git a/test/UnitTests/QueryParameters/IncludeServiceTests.cs b/test/UnitTests/QueryParameters/IncludeServiceTests.cs index 439dc98288..6771d53c7d 100644 --- a/test/UnitTests/QueryParameters/IncludeServiceTests.cs +++ b/test/UnitTests/QueryParameters/IncludeServiceTests.cs @@ -12,7 +12,7 @@ namespace UnitTests.QueryParameters public class IncludeServiceTests : QueryParametersUnitTestCollection { - public IncludeService GetService(ContextEntity resourceContext = null) + public IncludeService GetService(ResourceContext resourceContext = null) { return new IncludeService(_resourceGraph, MockCurrentRequest(resourceContext ?? _articleResourceContext)); } @@ -58,7 +58,7 @@ public void Parse_ChainsOnWrongMainResource_ThrowsJsonApiException() // arrange const string chain = "author.blogs.reviewer.favorite-food,reviewer.blogs.author.favorite-song"; var query = new KeyValuePair("include", new StringValues(chain)); - var service = GetService(_resourceGraph.GetContextEntity()); + var service = GetService(_resourceGraph.GetResourceContext()); // act, assert var exception = Assert.Throws( () => service.Parse(query)); diff --git a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs index 43bcf2e28f..24136f87d8 100644 --- a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs +++ b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs @@ -12,7 +12,7 @@ namespace UnitTests.QueryParameters { public class QueryParametersUnitTestCollection { - protected readonly ContextEntity _articleResourceContext; + protected readonly ResourceContext _articleResourceContext; protected readonly IResourceGraph _resourceGraph; public QueryParametersUnitTestCollection() @@ -24,10 +24,10 @@ public QueryParametersUnitTestCollection() builder.AddResource(); builder.AddResource(); _resourceGraph = builder.Build(); - _articleResourceContext = _resourceGraph.GetContextEntity
(); + _articleResourceContext = _resourceGraph.GetResourceContext
(); } - public ICurrentRequest MockCurrentRequest(ContextEntity requestResource = null) + public ICurrentRequest MockCurrentRequest(ResourceContext requestResource = null) { var mock = new Mock(); diff --git a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs index a4d1994e6f..dfef70051e 100644 --- a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs +++ b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs @@ -10,9 +10,9 @@ namespace UnitTests.QueryParameters { public class SparseFieldsServiceTests : QueryParametersUnitTestCollection { - public SparseFieldsService GetService(ContextEntity contextEntity = null) + public SparseFieldsService GetService(ResourceContext resourceContext = null) { - return new SparseFieldsService(_resourceGraph, MockCurrentRequest(contextEntity ?? _articleResourceContext)); + return new SparseFieldsService(_resourceGraph, MockCurrentRequest(resourceContext ?? _articleResourceContext)); } [Fact] @@ -40,13 +40,13 @@ public void Parse_ValidSelection_CanParse() var query = new KeyValuePair($"fields", new StringValues(attrName)); - var contextEntity = new ContextEntity + var resourceContext = new ResourceContext { - EntityName = type, + ResourceName = type, Attributes = new List { attribute, idAttribute }, Relationships = new List() }; - var service = GetService(contextEntity); + var service = GetService(resourceContext); // act service.Parse(query); @@ -70,13 +70,13 @@ public void Parse_TypeNameAsNavigation_Throws400ErrorWithRelationshipsOnlyMessag var query = new KeyValuePair($"fields[{type}]", new StringValues(attrName)); - var contextEntity = new ContextEntity + var resourceContext = new ResourceContext { - EntityName = type, + ResourceName = type, Attributes = new List { attribute, idAttribute }, Relationships = new List() }; - var service = GetService(contextEntity); + var service = GetService(resourceContext); // act, assert var ex = Assert.Throws(() => service.Parse(query)); @@ -96,13 +96,13 @@ public void Parse_DeeplyNestedSelection_Throws400ErrorWithDeeplyNestedMessage() var query = new KeyValuePair($"fields[{relationship}]", new StringValues(attrName)); - var contextEntity = new ContextEntity + var resourceContext = new ResourceContext { - EntityName = type, + ResourceName = type, Attributes = new List { attribute, idAttribute }, Relationships = new List() }; - var service = GetService(contextEntity); + var service = GetService(resourceContext); // act, assert var ex = Assert.Throws(() => service.Parse(query)); @@ -118,14 +118,14 @@ public void Parse_InvalidField_ThrowsJsonApiException() var query = new KeyValuePair($"fields[{type}]", new StringValues(attrName)); - var contextEntity = new ContextEntity + var resourceContext = new ResourceContext { - EntityName = type, + ResourceName = type, Attributes = new List(), Relationships = new List() }; - var service = GetService(contextEntity); + var service = GetService(resourceContext); // act , assert var ex = Assert.Throws(() => service.Parse(query)); diff --git a/test/UnitTests/ResourceHooks/AffectedEntitiesHelperTests.cs b/test/UnitTests/ResourceHooks/AffectedEntitiesHelperTests.cs index d4f579d121..bf81006448 100644 --- a/test/UnitTests/ResourceHooks/AffectedEntitiesHelperTests.cs +++ b/test/UnitTests/ResourceHooks/AffectedEntitiesHelperTests.cs @@ -40,20 +40,20 @@ public RelationshipDictionaryTests() { FirstToOneAttr = new HasOneAttribute("first-to-one") { - PrincipalType = typeof(Dummy), - DependentType = typeof(ToOne), + LeftType = typeof(Dummy), + RightType = typeof(ToOne), InternalRelationshipName = "FirstToOne" }; SecondToOneAttr = new HasOneAttribute("second-to-one") { - PrincipalType = typeof(Dummy), - DependentType = typeof(ToOne), + LeftType = typeof(Dummy), + RightType = typeof(ToOne), InternalRelationshipName = "SecondToOne" }; ToManyAttr = new HasManyAttribute("to-manies") { - PrincipalType = typeof(Dummy), - DependentType = typeof(ToMany), + LeftType = typeof(Dummy), + RightType = typeof(ToMany), InternalRelationshipName = "ToManies" }; Relationships.Add(FirstToOneAttr, FirstToOnesEntities); diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 0e5d84b08e..c341106d0e 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -5,7 +5,6 @@ using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Services; using JsonApiDotNetCore.Hooks; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; @@ -141,9 +140,9 @@ protected List CreateTodoWithOwner() public class HooksTestsSetup : HooksDummyData { - (Mock, Mock, Mock, IJsonApiOptions) CreateMocks() + (Mock, Mock, Mock, IJsonApiOptions) CreateMocks() { - var pfMock = new Mock(); + var pfMock = new Mock(); var ufMock = new Mock(); var iqsMock = new Mock(); var optionsMock = new JsonApiOptions { LoaDatabaseValues = false }; @@ -156,7 +155,7 @@ public class HooksTestsSetup : HooksDummyData // creates the resource definition mock and corresponding ImplementedHooks discovery instance var mainResource = CreateResourceDefinition(mainDiscovery); - // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + // mocking the genericServiceFactory and JsonApiContext and wiring them up. var (ufMock, iqMock, gpfMock, options) = CreateMocks(); SetupProcessorFactoryForResourceDefinition(gpfMock, mainResource.Object, mainDiscovery, null); @@ -181,7 +180,7 @@ public class HooksTestsSetup : HooksDummyData var mainResource = CreateResourceDefinition(mainDiscovery); var nestedResource = CreateResourceDefinition(nestedDiscovery); - // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + // mocking the genericServiceFactory and JsonApiContext and wiring them up. var (ufMock, iqMock, gpfMock, options) = CreateMocks(); var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; @@ -212,7 +211,7 @@ public class HooksTestsSetup : HooksDummyData var firstNestedResource = CreateResourceDefinition(firstNestedDiscovery); var secondNestedResource = CreateResourceDefinition(secondNestedDiscovery); - // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + // mocking the genericServiceFactory and JsonApiContext and wiring them up. var (ufMock, iqMock, gpfMock, options) = CreateMocks(); var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; @@ -228,10 +227,10 @@ public class HooksTestsSetup : HooksDummyData return (iqMock, hookExecutor, mainResource, firstNestedResource, secondNestedResource); } - protected IHooksDiscovery SetDiscoverableHooks(ResourceHook[] implementedHooks, params ResourceHook[] enableDbValuesHooks) - where TEntity : class, IIdentifiable + protected IHooksDiscovery SetDiscoverableHooks(ResourceHook[] implementedHooks, params ResourceHook[] enableDbValuesHooks) + where TResource : class, IIdentifiable { - var mock = new Mock>(); + var mock = new Mock>(); mock.Setup(discovery => discovery.ImplementedHooks) .Returns(implementedHooks); @@ -311,17 +310,17 @@ void MockHooks(Mock> resourceDefinition) } void SetupProcessorFactoryForResourceDefinition( - Mock processorFactory, + Mock processorFactory, IResourceHookContainer modelResource, IHooksDiscovery discovery, AppDbContext dbContext = null ) where TModel : class, IIdentifiable { - processorFactory.Setup(c => c.GetProcessor(typeof(ResourceDefinition<>), typeof(TModel))) + processorFactory.Setup(c => c.Get(typeof(ResourceDefinition<>), typeof(TModel))) .Returns(modelResource); - processorFactory.Setup(c => c.GetProcessor(typeof(IHooksDiscovery<>), typeof(TModel))) + processorFactory.Setup(c => c.Get(typeof(IHooksDiscovery<>), typeof(TModel))) .Returns(discovery); if (dbContext != null) @@ -329,8 +328,8 @@ void SetupProcessorFactoryForResourceDefinition( var idType = TypeHelper.GetIdentifierType(); if (idType == typeof(int)) { - IEntityReadRepository repo = CreateTestRepository(dbContext); - processorFactory.Setup(c => c.GetProcessor>(typeof(IEntityReadRepository<,>), typeof(TModel), typeof(int))).Returns(repo); + IResourceReadRepository repo = CreateTestRepository(dbContext); + processorFactory.Setup(c => c.Get>(typeof(IResourceReadRepository<,>), typeof(TModel), typeof(int))).Returns(repo); } else { @@ -340,12 +339,12 @@ void SetupProcessorFactoryForResourceDefinition( } } - IEntityReadRepository CreateTestRepository( + IResourceReadRepository CreateTestRepository( AppDbContext dbContext ) where TModel : class, IIdentifiable { IDbContextResolver resolver = CreateTestDbResolver(dbContext); - return new DefaultEntityRepository(null, resolver, null, null, null); + return new DefaultResourceRepository(null, resolver, null, null, null); } IDbContextResolver CreateTestDbResolver(AppDbContext dbContext) where TModel : class, IIdentifiable @@ -383,13 +382,13 @@ protected List> GetIncludedRelationshipsChains(param protected List GetIncludedRelationshipsChain(string chain) { var parsedChain = new List(); - var resourceContext = _resourceGraph.GetContextEntity(); + var resourceContext = _resourceGraph.GetResourceContext(); var splittedPath = chain.Split(QueryConstants.DOT); foreach (var requestedRelationship in splittedPath) { var relationship = resourceContext.Relationships.Single(r => r.PublicRelationshipName == requestedRelationship); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); + resourceContext = _resourceGraph.GetResourceContext(relationship.RightType); } return parsedChain; } diff --git a/test/UnitTests/Serialization/Common/DocumentParserTests.cs b/test/UnitTests/Serialization/Common/DocumentParserTests.cs index c16daf36c7..fa6e10ce01 100644 --- a/test/UnitTests/Serialization/Common/DocumentParserTests.cs +++ b/test/UnitTests/Serialization/Common/DocumentParserTests.cs @@ -132,7 +132,7 @@ public void DeserializeAttributes_VariousDataTypes_CanDeserialize(string member, var entity = (TestResource)_deserializer.Deserialize(body); // assert - var pi = _resourceGraph.GetContextEntity("test-resource").Attributes.Single(attr => attr.PublicAttributeName == member).PropertyInfo; + var pi = _resourceGraph.GetResourceContext("test-resource").Attributes.Single(attr => attr.PublicAttributeName == member).PropertyInfo; var deserializedValue = pi.GetValue(entity); if (member == "int-field") diff --git a/test/UnitTests/Serialization/SerializerTestsSetup.cs b/test/UnitTests/Serialization/SerializerTestsSetup.cs index d359d46a2c..133482a5d1 100644 --- a/test/UnitTests/Serialization/SerializerTestsSetup.cs +++ b/test/UnitTests/Serialization/SerializerTestsSetup.cs @@ -49,7 +49,7 @@ protected ResponseSerializer GetResponseSerializer(List(meta, link, includedBuilder, fieldsToSerialize, resourceObjectBuilder, provider); } @@ -74,7 +74,7 @@ protected IResourceObjectBuilderSettingsProvider GetSerializerSettingsProvider() return mock.Object; } - private IResourceGraph GetContextEntityProvider() + private IResourceGraph GetResourceContextProvider() { return _resourceGraph; } @@ -89,14 +89,14 @@ protected IMetaBuilder GetMetaBuilder(Dictionary meta = nu protected ICurrentRequest GetRequestManager() where T : class, IIdentifiable { var mock = new Mock(); - mock.Setup(m => m.GetRequestResource()).Returns(_resourceGraph.GetContextEntity()); + mock.Setup(m => m.GetRequestResource()).Returns(_resourceGraph.GetResourceContext()); return mock.Object; } protected ILinkBuilder GetLinkBuilder(TopLevelLinks top = null, ResourceLinks resource = null, RelationshipLinks relationship = null) { var mock = new Mock(); - mock.Setup(m => m.GetTopLevelLinks(It.IsAny())).Returns(top); + mock.Setup(m => m.GetTopLevelLinks(It.IsAny())).Returns(top); mock.Setup(m => m.GetResourceLinks(It.IsAny(), It.IsAny())).Returns(resource); mock.Setup(m => m.GetRelationshipLinks(It.IsAny(), It.IsAny())).Returns(relationship); return mock.Object; @@ -111,8 +111,8 @@ protected ISparseFieldsService GetFieldsQuery() protected IFieldsToSerialize GetSerializableFields() { var mock = new Mock(); - mock.Setup(m => m.GetAllowedAttributes(It.IsAny(), It.IsAny())).Returns((t, r) => _resourceGraph.GetContextEntity(t).Attributes); - mock.Setup(m => m.GetAllowedRelationships(It.IsAny())).Returns(t => _resourceGraph.GetContextEntity(t).Relationships); + mock.Setup(m => m.GetAllowedAttributes(It.IsAny(), It.IsAny())).Returns((t, r) => _resourceGraph.GetResourceContext(t).Attributes); + mock.Setup(m => m.GetAllowedRelationships(It.IsAny())).Returns(t => _resourceGraph.GetResourceContext(t).Relationships); return mock.Object; } @@ -131,7 +131,7 @@ protected IIncludeService GetIncludedRelationships(List protected class TestDocumentBuilder : BaseDocumentBuilder { - public TestDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder, IContextEntityProvider provider) : base(resourceObjectBuilder, provider) { } + public TestDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder, IResourceContextProvider provider) : base(resourceObjectBuilder, provider) { } public new Document Build(IIdentifiable entity, List attributes = null, List relationships = null) { diff --git a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs index 13e079c927..e3b7798974 100644 --- a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs @@ -157,13 +157,13 @@ public void BuildIncluded_DuplicateChildrenMultipleChains_OnceInOutput() private List GetIncludedRelationshipsChain(string chain) { var parsedChain = new List(); - var resourceContext = _resourceGraph.GetContextEntity
(); + var resourceContext = _resourceGraph.GetResourceContext
(); var splittedPath = chain.Split(QueryConstants.DOT); foreach (var requestedRelationship in splittedPath) { var relationship = resourceContext.Relationships.Single(r => r.PublicRelationshipName == requestedRelationship); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); + resourceContext = _resourceGraph.GetResourceContext(relationship.RightType); } return parsedChain; } diff --git a/test/UnitTests/Services/EntityResourceService_Tests.cs b/test/UnitTests/Services/EntityResourceService_Tests.cs index 2ff9f83cb6..19bbe41689 100644 --- a/test/UnitTests/Services/EntityResourceService_Tests.cs +++ b/test/UnitTests/Services/EntityResourceService_Tests.cs @@ -20,7 +20,7 @@ namespace UnitTests.Services { public class EntityResourceService_Tests { - private readonly Mock> _repositoryMock = new Mock>(); + private readonly Mock> _repositoryMock = new Mock>(); private readonly ILoggerFactory _loggerFactory = new Mock().Object; private readonly Mock _crMock; private readonly Mock _pgsMock; @@ -95,9 +95,9 @@ public async Task GetRelationshipAsync_Returns_Relationship_Value() Assert.Equal(todoItem.Collection.Id, collection.Id); } - private EntityResourceService GetService() + private DefaultResourceService GetService() { - return new EntityResourceService(null, null, _repositoryMock.Object, new JsonApiOptions(), null, null, _pgsMock.Object, _resourceGraph); + return new DefaultResourceService(null, null, _repositoryMock.Object, new JsonApiOptions(), null, null, _pgsMock.Object, _resourceGraph); } } } From a37a7c964d9c71f557902c7fb72550ffcfe0ccdd Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 17:22:31 +0200 Subject: [PATCH 2/2] chore: process feedback --- .../Properties/launchSettings.json | 14 +++++++------- .../Data/DefaultResourceRepository.cs | 12 ++++++------ src/JsonApiDotNetCore/Data/IDbContextResolver.cs | 4 ---- .../Data/IResourceReadRepository.cs | 4 ++-- .../Extensions/IQueryableExtensions.cs | 2 +- .../QueryParameterServices/SparseFieldsService.cs | 4 ++-- .../Services/DefaultResourceService.cs | 4 ++-- .../Acceptance/Spec/SparseFieldSetTests.cs | 2 +- .../Services/EntityResourceService_Tests.cs | 4 ++-- 9 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json b/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json index 0abc738c49..1dff6cfe69 100644 --- a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json +++ b/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json @@ -8,13 +8,6 @@ } }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, "NoEntityFrameworkExample": { "commandName": "Project", "launchBrowser": true, @@ -22,6 +15,13 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:5000/" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } } } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs index 117fb450e3..cefdc2c419 100644 --- a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs @@ -55,9 +55,9 @@ public DefaultResourceRepository( public virtual IQueryable Get(TId id) => _dbSet.Where(e => e.Id.Equals(id)); /// - public virtual IQueryable Select(IQueryable entities, params AttrAttribute[] fields) + public virtual IQueryable Select(IQueryable entities, IEnumerable fields = null) { - if (fields.Any()) + if (fields != null && fields.Any()) return entities.Select(fields); return entities; @@ -220,7 +220,7 @@ private object GetTrackedRelationshipValue(RelationshipAttribute relationshipAtt var relationshipValue = (IIdentifiable)hasOneAttr.GetValue(entity); if (relationshipValue == null) return null; - return GetTrackedHasOneRelationshipValue(relationshipValue, hasOneAttr, ref wasAlreadyAttached); + return GetTrackedHasOneRelationshipValue(relationshipValue, ref wasAlreadyAttached); } IEnumerable relationshipValueList = (IEnumerable)relationshipAttr.GetValue(entity); @@ -246,7 +246,7 @@ private IList GetTrackedManyRelationshipValue(IEnumerable relatio } // helper method used in GetTrackedRelationshipValue. See comments there. - private IIdentifiable GetTrackedHasOneRelationshipValue(IIdentifiable relationshipValue, HasOneAttribute hasOneAttr, ref bool wasAlreadyAttached) + private IIdentifiable GetTrackedHasOneRelationshipValue(IIdentifiable relationshipValue, ref bool wasAlreadyAttached) { var tracked = AttachOrGetTracked(relationshipValue); if (tracked != null) wasAlreadyAttached = true; @@ -282,9 +282,9 @@ public virtual async Task DeleteAsync(TId id) return true; } - public virtual IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain) + public virtual IQueryable Include(IQueryable entities, IEnumerable inclusionChain = null) { - if (!inclusionChain.Any()) + if (inclusionChain == null || !inclusionChain.Any()) return entities; string internalRelationshipPath = null; diff --git a/src/JsonApiDotNetCore/Data/IDbContextResolver.cs b/src/JsonApiDotNetCore/Data/IDbContextResolver.cs index cb5372c29d..7d2f5a66dc 100644 --- a/src/JsonApiDotNetCore/Data/IDbContextResolver.cs +++ b/src/JsonApiDotNetCore/Data/IDbContextResolver.cs @@ -6,9 +6,5 @@ namespace JsonApiDotNetCore.Data public interface IDbContextResolver { DbContext GetContext(); - - [Obsolete("Use DbContext.Set() instead", error: true)] - DbSet GetDbSet() - where TResource : class; } } diff --git a/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs index 6480131c7e..d2d8946d2d 100644 --- a/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs +++ b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs @@ -26,7 +26,7 @@ public interface IResourceReadRepository /// /// Apply fields to the provided queryable /// - IQueryable Select(IQueryable entities, params AttrAttribute[] fields); + IQueryable Select(IQueryable entities, IEnumerable fields); /// /// Include a relationship in the query /// @@ -35,7 +35,7 @@ public interface IResourceReadRepository /// _todoItemsRepository.GetAndIncludeAsync(1, "achieved-date"); /// /// - IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain); + IQueryable Include(IQueryable entities, IEnumerable inclusionChain); /// /// Apply a filter to the provided queryable /// diff --git a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs index bb00eef3f3..49f6dfc326 100644 --- a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs @@ -64,7 +64,7 @@ public static IQueryable Filter(this IQueryable sourc return CallGenericWhereMethod(source, filterQuery); } - public static IQueryable Select(this IQueryable source, AttrAttribute[] columns) + public static IQueryable Select(this IQueryable source, IEnumerable columns) => CallGenericSelectMethod(source, columns.Select(attr => attr.InternalAttributeName).ToList()); public static IOrderedQueryable Sort(this IQueryable source, SortQueryContext sortQuery) diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs index f20542562d..83b5fcdff3 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs @@ -62,7 +62,7 @@ public virtual void Parse(KeyValuePair queryParameter) // that is equal to the resource name, like with self-referering data types (eg directory structures) // if not, no longer support this type of sparse field selection. if (navigation == _requestResource.ResourceName && !_requestResource.Relationships.Any(a => a.Is(navigation))) - throw new JsonApiException(400, $"Use \"?fields=...\" instead of \"fields[{navigation}]\":" + + throw new JsonApiException(400, $"Use '?fields=...' instead of 'fields[{navigation}]':" + $" the square bracket navigations is now reserved " + $"for relationships only. See https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/555#issuecomment-543100865"); @@ -71,7 +71,7 @@ public virtual void Parse(KeyValuePair queryParameter) var relationship = _requestResource.Relationships.SingleOrDefault(a => a.Is(navigation)); if (relationship == null) - throw new JsonApiException(400, $"\"{navigation}\" in \"fields[{navigation}]\" is not a valid relationship of {_requestResource.ResourceName}"); + throw new JsonApiException(400, $"'{navigation}' in 'fields[{navigation}]' is not a valid relationship of {_requestResource.ResourceName}"); foreach (var field in fields) RegisterRelatedResourceField(field, relationship); diff --git a/src/JsonApiDotNetCore/Services/DefaultResourceService.cs b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs index 5c79f0dc5c..bb68d08754 100644 --- a/src/JsonApiDotNetCore/Services/DefaultResourceService.cs +++ b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs @@ -136,7 +136,7 @@ public virtual async Task GetRelationshipsAsync(TId id, string relati // TODO: it would be better if we could distinguish whether or not the relationship was not found, // vs the relationship not being set on the instance of T - var entityQuery = _repository.Include(_repository.Get(id), relationship); + var entityQuery = _repository.Include(_repository.Get(id), new RelationshipAttribute[] { relationship }); var entity = await _repository.FirstOrDefaultAsync(entityQuery); if (entity == null) // this does not make sense. If the parent entity is not found, this error is thrown? throw new JsonApiException(404, $"Relationship '{relationshipName}' not found."); @@ -174,7 +174,7 @@ public virtual async Task UpdateAsync(TId id, TResource entity) public virtual async Task UpdateRelationshipsAsync(TId id, string relationshipName, object related) { var relationship = GetRelationship(relationshipName); - var entityQuery = _repository.Include(_repository.Get(id), relationship); + var entityQuery = _repository.Include(_repository.Get(id), new RelationshipAttribute[] { relationship }); var entity = await _repository.FirstOrDefaultAsync(entityQuery); if (entity == null) throw new JsonApiException(404, $"Entity with id {id} could not be found."); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs index 16c83ad13a..5b9ea5ea57 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs @@ -73,7 +73,7 @@ public async Task Can_Select_Sparse_Fieldsets() var query = _dbContext .TodoItems .Where(t => t.Id == todoItem.Id) - .Select(_resourceGraph.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } ).ToArray()); + .Select(_resourceGraph.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } )); var resultSql = StringExtensions.Normalize(query.ToSql()); var result = await query.FirstAsync(); diff --git a/test/UnitTests/Services/EntityResourceService_Tests.cs b/test/UnitTests/Services/EntityResourceService_Tests.cs index 19bbe41689..169bb66179 100644 --- a/test/UnitTests/Services/EntityResourceService_Tests.cs +++ b/test/UnitTests/Services/EntityResourceService_Tests.cs @@ -45,7 +45,7 @@ public async Task GetRelationshipAsync_Passes_Public_ResourceName_To_Repository( // arrange const int id = 1; const string relationshipName = "collection"; - var relationship = new HasOneAttribute(relationshipName); + var relationship = new RelationshipAttribute[] { new HasOneAttribute(relationshipName) }; var todoItem = new TodoItem(); var query = new List { todoItem }.AsQueryable(); @@ -71,7 +71,7 @@ public async Task GetRelationshipAsync_Returns_Relationship_Value() // arrange const int id = 1; const string relationshipName = "collection"; - var relationship = new HasOneAttribute(relationshipName); + var relationship = new RelationshipAttribute[] { new HasOneAttribute(relationshipName) }; var todoItem = new TodoItem {