From eed794a4c5133f72e9026d1376db5d45e587c2d5 Mon Sep 17 00:00:00 2001 From: Harro van der Kroft Date: Wed, 20 Feb 2019 17:04:21 +0100 Subject: [PATCH 001/168] feat(business-logic): added more integration with ResourceDefinition --- JsonApiDotnetCore.sln | 28 +++++++--- README.md | 7 +-- docs/usage/extensibility/services.md | 4 +- .../Properties/launchSettings.json | 27 +++++++++ .../Resources/TagResource.cs | 13 +++++ .../Resources/UserResource.cs | 3 + .../Data/DefaultEntityRepository.cs | 34 ++++++++--- .../Models/ResourceDefinition.cs | 19 ++++++- ...rs_Tests.cs => ResourceDefinitionTests.cs} | 56 ++++++++++++++++++- 9 files changed, 162 insertions(+), 29 deletions(-) create mode 100644 src/Examples/GettingStarted/Properties/launchSettings.json create mode 100644 src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs rename test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/{OutputAttrs_Tests.cs => ResourceDefinitionTests.cs} (71%) diff --git a/JsonApiDotnetCore.sln b/JsonApiDotnetCore.sln index bad0e19743..e5c7339d0c 100644 --- a/JsonApiDotnetCore.sln +++ b/JsonApiDotnetCore.sln @@ -1,6 +1,7 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28606.126 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore", "src\JsonApiDotNetCore\JsonApiDotNetCore.csproj", "{C0EC9E70-EB2E-436F-9D94-FA16FA774123}" EndProject @@ -41,13 +42,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExample", "src\Ex EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExampleTests", "test\OperationsExampleTests\OperationsExampleTests.csproj", "{9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEntitySeparationExample", "src\Examples\ResourceEntitySeparationExample\ResourceEntitySeparationExample.csproj", "{F4097194-9415-418A-AB4E-315C5D5466AF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResourceEntitySeparationExample", "src\Examples\ResourceEntitySeparationExample\ResourceEntitySeparationExample.csproj", "{F4097194-9415-418A-AB4E-315C5D5466AF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEntitySeparationExampleTests", "test\ResourceEntitySeparationExampleTests\ResourceEntitySeparationExampleTests.csproj", "{6DFA30D7-1679-4333-9779-6FB678E48EF5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResourceEntitySeparationExampleTests", "test\ResourceEntitySeparationExampleTests\ResourceEntitySeparationExampleTests.csproj", "{6DFA30D7-1679-4333-9779-6FB678E48EF5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStarted", "src\Examples\GettingStarted\GettingStarted.csproj", "{DF9BFD82-D937-4907-B0B4-64670417115F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GettingStarted", "src\Examples\GettingStarted\GettingStarted.csproj", "{DF9BFD82-D937-4907-B0B4-64670417115F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscoveryTests", "test\DiscoveryTests\DiscoveryTests.csproj", "{09C0C8D8-B721-4955-8889-55CB149C3B5C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscoveryTests", "test\DiscoveryTests\DiscoveryTests.csproj", "{09C0C8D8-B721-4955-8889-55CB149C3B5C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -190,7 +191,18 @@ Global {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x64.ActiveCfg = Release|Any CPU {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x64.Build.0 = Release|Any CPU {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.ActiveCfg = Release|Any CPU - {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.Build.0 = Release|Any CPU\ + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x64.Build.0 = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x86.ActiveCfg = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x86.Build.0 = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|Any CPU.Build.0 = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x64.ActiveCfg = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x64.Build.0 = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x86.ActiveCfg = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x86.Build.0 = Release|Any CPU {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|Any CPU.Build.0 = Debug|Any CPU {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|x64.ActiveCfg = Debug|Any CPU diff --git a/README.md b/README.md index 5f7a929bb1..d0a5bb2cf2 100644 --- a/README.md +++ b/README.md @@ -91,12 +91,7 @@ Running tests locally requires access to a postgresql database. If you have docker installed, this can be propped up via: ```bash -docker run --rm --name jsonapi-dotnet-core-testing \ - -e POSTGRES_DB=JsonApiDotNetCoreExample \ - -e POSTGRES_USER=postgres \ - -e POSTGRES_PASSWORD=postgres \ - -p 5432:5432 \ - postgres +docker run --rm --name jsonapi-dotnet-core-testing -e POSTGRES_DB=JsonApiDotNetCoreExample -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres ``` And then to run the tests: diff --git a/docs/usage/extensibility/services.md b/docs/usage/extensibility/services.md index 81685a6f36..79fa7d8fb7 100644 --- a/docs/usage/extensibility/services.md +++ b/docs/usage/extensibility/services.md @@ -17,7 +17,7 @@ public class TodoItemService : EntityResourceService public TodoItemService( IJsonApiContext jsonApiContext, - IEntityRepository repository, + IEntityRepository repository, ILoggerFactory loggerFactory, INotificationService notificationService) : base(jsonApiContext, repository, loggerFactory) @@ -25,7 +25,7 @@ public class TodoItemService : EntityResourceService _notificationService = notificationService; } - public override async Task CreateAsync(TEntity entity) + public override async Task CreateAsync(TodoItem entity) { // call the base implementation which uses Entity Framework var newEntity = await base.CreateAsync(entity); diff --git a/src/Examples/GettingStarted/Properties/launchSettings.json b/src/Examples/GettingStarted/Properties/launchSettings.json new file mode 100644 index 0000000000..b1886d6c3b --- /dev/null +++ b/src/Examples/GettingStarted/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:56042/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "GettingStarted": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:56046/" + } + } +} \ No newline at end of file diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs new file mode 100644 index 0000000000..872487218c --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Linq; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCoreExample.Models; + +namespace JsonApiDotNetCoreExample.Resources +{ + public class TagResource : ResourceDefinition + { + + public override IQueryable OnList(IQueryable entities) => entities.Where(t => t.Name != "THISTAGSHOULDNOTBEVISIBLE"); + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index 030bc4eaa4..eca5e6ce44 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using JsonApiDotNetCore.Models; using JsonApiDotNetCoreExample.Models; @@ -8,5 +9,7 @@ public class UserResource : ResourceDefinition { protected override List OutputAttrs() => Remove(user => user.Password); + + public override IQueryable OnList(IQueryable entities) => entities; } } diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index fd3a8b48e8..9d8d8bbb2b 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -81,10 +81,16 @@ public DefaultEntityRepository( /// public virtual IQueryable Get() { + var entities = (IQueryable)_dbSet; + if (_resourceDefinition != null) + { + entities = _resourceDefinition.OnList(entities); + } + if (_jsonApiContext.QuerySet?.Fields != null && _jsonApiContext.QuerySet.Fields.Count > 0) - return _dbSet.Select(_jsonApiContext.QuerySet?.Fields); + return entities.Select(_jsonApiContext.QuerySet?.Fields); - return _dbSet; + return entities; } /// @@ -163,7 +169,7 @@ public void DetachRelationshipPointers(TEntity entity) { foreach (var hasOneRelationship in _jsonApiContext.HasOneRelationshipPointers.Get()) { - var hasOne = (HasOneAttribute) hasOneRelationship.Key; + var hasOne = (HasOneAttribute)hasOneRelationship.Key; if (hasOne.EntityPropertyName != null) { var relatedEntity = entity.GetType().GetProperty(hasOne.EntityPropertyName)?.GetValue(entity); @@ -178,7 +184,7 @@ public void DetachRelationshipPointers(TEntity entity) foreach (var hasManyRelationship in _jsonApiContext.HasManyRelationshipPointers.Get()) { - var hasMany = (HasManyAttribute) hasManyRelationship.Key; + var hasMany = (HasManyAttribute)hasManyRelationship.Key; if (hasMany.EntityPropertyName != null) { var relatedList = (IList)entity.GetType().GetProperty(hasMany.EntityPropertyName)?.GetValue(entity); @@ -194,7 +200,7 @@ public void DetachRelationshipPointers(TEntity entity) _context.Entry(pointer).State = EntityState.Detached; } } - + // HACK: detaching has many relationships doesn't appear to be sufficient // the navigation property actually needs to be nulled out, otherwise // EF adds duplicate instances to the collection @@ -234,7 +240,7 @@ private void AttachHasMany(TEntity entity, HasManyAttribute relationship, IList { _context.Entry(pointer).State = EntityState.Unchanged; } - } + } } private void AttachHasManyThrough(TEntity entity, HasManyThroughAttribute hasManyThrough, IList pointers) @@ -270,7 +276,7 @@ private void AttachHasOnePointers(TEntity entity) if (relationship.Key.GetType() != typeof(HasOneAttribute)) continue; - var hasOne = (HasOneAttribute) relationship.Key; + var hasOne = (HasOneAttribute)relationship.Key; if (hasOne.EntityPropertyName != null) { var relatedEntity = entity.GetType().GetProperty(hasOne.EntityPropertyName)?.GetValue(entity); @@ -347,10 +353,16 @@ public virtual IQueryable Include(IQueryable entities, string // TODO: make recursive method string internalRelationshipPath = null; var entity = _jsonApiContext.RequestEntity; + + var typeTree = new Dictionary() { }; + for (var i = 0; i < relationshipChain.Length; i++) { var requestedRelationship = relationshipChain[i]; var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == requestedRelationship); + + // need to add this to the typetree so we know what we're dealing with + typeTree.Add(relationship.Type); if (relationship == null) { throw new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {entity.EntityName}", @@ -366,11 +378,15 @@ public virtual IQueryable Include(IQueryable entities, string ? relationship.RelationshipPath : $"{internalRelationshipPath}.{relationship.RelationshipPath}"; - if(i < relationshipChain.Length) + if (i < relationshipChain.Length) entity = _jsonApiContext.ResourceGraph.GetContextEntity(relationship.Type); } - return entities.Include(internalRelationshipPath); + entities = entities.Include(internalRelationshipPath); + // here we also need to check for any filters + + + return entities; } /// diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index cb4894ce27..8c9a3c7aef 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -49,9 +49,16 @@ private bool InstanceOutputAttrsAreSpecified() .FirstOrDefault(); var declaringType = instanceMethod?.DeclaringType; return declaringType == derivedType; - } + } - // TODO: need to investigate options for caching these + /// + /// Remove an attribute + /// + /// @TODO: need to investigate options for caching these + /// + /// the filter to execute + /// @TODO + /// protected List Remove(Expression> filter, List from = null) { from = from ?? _contextEntity.Attributes; @@ -157,6 +164,14 @@ private List GetOutputAttrs() /// public virtual QueryFilters GetQueryFilters() => null; + /// + /// Executed when listing all resources + /// + /// + /// + public virtual IQueryable OnList(IQueryable entities) => entities; + + /// /// This is an alias type intended to simplify the implementation's /// method signature. diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/OutputAttrs_Tests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs similarity index 71% rename from test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/OutputAttrs_Tests.cs rename to test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs index c5a6c054a4..c93f7f192b 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/OutputAttrs_Tests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs @@ -16,13 +16,17 @@ namespace JsonApiDotNetCoreExampleTests.Acceptance { [Collection("WebHostCollection")] - public class OutputAttrs_Tests + public class ResourceDefinitionTests { private TestFixture _fixture; private AppDbContext _context; private Faker _userFaker; + private static readonly Faker
_articleFaker = new Faker
() + .RuleFor(a => a.Name, f => f.Random.AlphaNumeric(10)) + .RuleFor(a => a.Author, f => new Author()); - public OutputAttrs_Tests(TestFixture fixture) + private static readonly Faker _tagFaker = new Faker().RuleFor(a => a.Name, f => f.Random.AlphaNumeric(10)); + public ResourceDefinitionTests(TestFixture fixture) { _fixture = fixture; _context = fixture.GetService(); @@ -31,6 +35,54 @@ public OutputAttrs_Tests(TestFixture fixture) .RuleFor(u => u.Password, f => f.Internet.Password()); } + [Fact] + public async Task Tag_Is_Hidden() + { + // Arrange + var context = _fixture.GetService(); + var article = _articleFaker.Generate(); + var tag = _tagFaker.Generate(); + + tag.Name = "THISTAGSHOULDNOTBEVISIBLE"; + + context.Articles.RemoveRange(context.Articles); + await context.SaveChangesAsync(); + + var articleTag = new ArticleTag + { + Article = article, + Tag = tag + }; + context.ArticleTags.Add(articleTag); + await context.SaveChangesAsync(); + + var route = $"/api/v1/articles?include=tags"; + + var httpMethod = new HttpMethod("GET"); + var request = new HttpRequestMessage(httpMethod, route); + + + // Act + var response = await _fixture.Client.GetAsync(route); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + Assert.True(HttpStatusCode.OK == response.StatusCode, $"{route} returned {response.StatusCode} status code with payload: {body}"); + + var document = JsonConvert.DeserializeObject(body); + Assert.NotEmpty(document.Included); + + var articleResponseList = _fixture.GetService().DeserializeList
(body); + Assert.NotNull(articleResponseList); + + var articleResponse = articleResponseList.FirstOrDefault(a => a.Id == article.Id); + Assert.NotNull(articleResponse); + Assert.Equal(article.Name, articleResponse.Name); + + Assert.Null(articleResponse.Tags); + } + + [Fact] public async Task Password_Is_Not_Included_In_Response_Payload() { From 9fee385cdc84ac23c04a7641875f9dfdd93a35ce Mon Sep 17 00:00:00 2001 From: Harro van der Kroft Date: Wed, 20 Feb 2019 17:48:38 +0100 Subject: [PATCH 002/168] feat(business-logic): added some preliminary code --- .../Logic/LogicCache.cs | 24 +++++++++ .../JsonApiDotNetCoreExample/Startup.cs | 3 ++ .../Data/DefaultEntityRepository.cs | 50 ++++++++++++++----- src/JsonApiDotNetCore/Logic/ILogicCache.cs | 29 +++++++++++ 4 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 src/Examples/JsonApiDotNetCoreExample/Logic/LogicCache.cs create mode 100644 src/JsonApiDotNetCore/Logic/ILogicCache.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Logic/LogicCache.cs b/src/Examples/JsonApiDotNetCoreExample/Logic/LogicCache.cs new file mode 100644 index 0000000000..0aed7acb30 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Logic/LogicCache.cs @@ -0,0 +1,24 @@ +using JsonApiDotNetCore.Logic; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCoreExample.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace JsonApiDotNetCoreExample.Logic +{ + public class LogicCache : BaseLogicCache + { + + /// + /// Hacky, but fast to show how it could work + /// + /// + public LogicCache(ResourceDefinition tagDefinition) + { + _cache.Add(typeof(Tag), tagDefinition); + } + + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startup.cs index 68ea93a7fc..21945d5a6a 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startup.cs @@ -7,6 +7,8 @@ using Microsoft.EntityFrameworkCore; using JsonApiDotNetCore.Extensions; using System; +using JsonApiDotNetCore.Logic; +using JsonApiDotNetCoreExample.Logic; namespace JsonApiDotNetCoreExample { @@ -43,6 +45,7 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services) mvcBuilder, discovery => discovery.AddCurrentAssembly()); + services.AddSingleton(); return services.BuildServiceProvider(); } diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 9d8d8bbb2b..3cff54f482 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -7,9 +7,11 @@ using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Internal.Query; +using JsonApiDotNetCore.Logic; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.Extensions.Logging; namespace JsonApiDotNetCore.Data @@ -23,16 +25,17 @@ public class DefaultEntityRepository public DefaultEntityRepository( IJsonApiContext jsonApiContext, IDbContextResolver contextResolver, - ResourceDefinition resourceDefinition = null) - : base(jsonApiContext, contextResolver, resourceDefinition) + ILogicCache logicCache = null) + : base(jsonApiContext, contextResolver, logicCache) { } public DefaultEntityRepository( ILoggerFactory loggerFactory, IJsonApiContext jsonApiContext, IDbContextResolver contextResolver, - ResourceDefinition resourceDefinition = null) - : base(loggerFactory, jsonApiContext, contextResolver, resourceDefinition) + ILogicCache logicCache = null + ) + : base(loggerFactory, jsonApiContext, contextResolver, logicCache) { } } @@ -50,32 +53,42 @@ public class DefaultEntityRepository private readonly ILogger _logger; private readonly IJsonApiContext _jsonApiContext; private readonly IGenericProcessorFactory _genericProcessorFactory; + private readonly ILogicCache _logicCache; private readonly ResourceDefinition _resourceDefinition; public DefaultEntityRepository( IJsonApiContext jsonApiContext, IDbContextResolver contextResolver, - ResourceDefinition resourceDefinition = null) + ILogicCache logicCache) { _context = contextResolver.GetContext(); _dbSet = contextResolver.GetDbSet(); _jsonApiContext = jsonApiContext; _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; - _resourceDefinition = resourceDefinition; + if (logicCache != null) + { + _logicCache = logicCache; + _resourceDefinition = (ResourceDefinition)logicCache.GetLogic(typeof(TEntity)); + } + } public DefaultEntityRepository( ILoggerFactory loggerFactory, IJsonApiContext jsonApiContext, IDbContextResolver contextResolver, - ResourceDefinition resourceDefinition = null) + ILogicCache logicCache) { _context = contextResolver.GetContext(); _dbSet = contextResolver.GetDbSet(); _jsonApiContext = jsonApiContext; _logger = loggerFactory.CreateLogger>(); _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; - _resourceDefinition = resourceDefinition; + if (logicCache != null) + { + _logicCache = logicCache; + _resourceDefinition = (ResourceDefinition)logicCache.GetLogic(typeof(TEntity)); + } } /// @@ -354,7 +367,7 @@ public virtual IQueryable Include(IQueryable entities, string string internalRelationshipPath = null; var entity = _jsonApiContext.RequestEntity; - var typeTree = new Dictionary() { }; + var logicTree = new Dictionary() { }; for (var i = 0; i < relationshipChain.Length; i++) { @@ -362,7 +375,11 @@ public virtual IQueryable Include(IQueryable entities, string var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == requestedRelationship); // need to add this to the typetree so we know what we're dealing with - typeTree.Add(relationship.Type); + var logic = _logicCache.GetLogic(relationship.Type); + if (logic != null) + { + logicTree.Add(relationship.Type, logic); + } if (relationship == null) { throw new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {entity.EntityName}", @@ -382,13 +399,22 @@ public virtual IQueryable Include(IQueryable entities, string entity = _jsonApiContext.ResourceGraph.GetContextEntity(relationship.Type); } - entities = entities.Include(internalRelationshipPath); - // here we also need to check for any filters + // here we also need to check for any filters + // we have access to the logicTree which should have the + // Required logic to handle nested changes to the entities + // we need to + entities = entities.Include(internalRelationshipPath); + // I mean, the including is fine, but we still need to apply a `Where` somehow + // we could dynamically build + // entities.Where(e = > logic.Apply(e)) or something, but I cant really figure out how atm return entities; } + + + /// public virtual async Task> PageAsync(IQueryable entities, int pageSize, int pageNumber) { diff --git a/src/JsonApiDotNetCore/Logic/ILogicCache.cs b/src/JsonApiDotNetCore/Logic/ILogicCache.cs new file mode 100644 index 0000000000..32dfc36dc1 --- /dev/null +++ b/src/JsonApiDotNetCore/Logic/ILogicCache.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Logic +{ + public interface ILogicCache + { + IResourceDefinition GetLogic(Type type); + } + public class BaseLogicCache : ILogicCache + { + protected Dictionary _cache = new Dictionary(); + + + public IResourceDefinition GetLogic(Type type) + { + var toReturn = _cache.FirstOrDefault(c => c.Key == type); + if(toReturn.Equals(default(KeyValuePair))) + { + return null; + } + return toReturn.Value; + } + } + +} From d7c93313e489562baf180b7b8f9316e2f987b1d0 Mon Sep 17 00:00:00 2001 From: Harro van der Kroft Date: Mon, 25 Feb 2019 18:39:07 +0100 Subject: [PATCH 003/168] feat: added first version of logic tree traversing --- .../Properties/launchSettings.json | 8 +- .../Logic/LogicCache.cs | 24 -- .../JsonApiDotNetCoreExample/Startup.cs | 3 - src/JsonApiDotNetCore/Data/Article.cs | 4 + .../Data/DefaultEntityRepository.cs | 255 ++++++++++++++---- .../Extensions/IQueryableExtensions.cs | 8 + src/JsonApiDotNetCore/Logic/ILogicCache.cs | 29 -- .../Models/ResourceDefinition.cs | 19 +- 8 files changed, 232 insertions(+), 118 deletions(-) delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Logic/LogicCache.cs create mode 100644 src/JsonApiDotNetCore/Data/Article.cs delete mode 100644 src/JsonApiDotNetCore/Logic/ILogicCache.cs diff --git a/src/Examples/GettingStarted/Properties/launchSettings.json b/src/Examples/GettingStarted/Properties/launchSettings.json index b1886d6c3b..7dc07f5b4f 100644 --- a/src/Examples/GettingStarted/Properties/launchSettings.json +++ b/src/Examples/GettingStarted/Properties/launchSettings.json @@ -17,11 +17,7 @@ }, "GettingStarted": { "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:56046/" + "launchBrowser": true } } -} \ No newline at end of file +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Logic/LogicCache.cs b/src/Examples/JsonApiDotNetCoreExample/Logic/LogicCache.cs deleted file mode 100644 index 0aed7acb30..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Logic/LogicCache.cs +++ /dev/null @@ -1,24 +0,0 @@ -using JsonApiDotNetCore.Logic; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCoreExample.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace JsonApiDotNetCoreExample.Logic -{ - public class LogicCache : BaseLogicCache - { - - /// - /// Hacky, but fast to show how it could work - /// - /// - public LogicCache(ResourceDefinition tagDefinition) - { - _cache.Add(typeof(Tag), tagDefinition); - } - - } -} diff --git a/src/Examples/JsonApiDotNetCoreExample/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startup.cs index 21945d5a6a..68ea93a7fc 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startup.cs @@ -7,8 +7,6 @@ using Microsoft.EntityFrameworkCore; using JsonApiDotNetCore.Extensions; using System; -using JsonApiDotNetCore.Logic; -using JsonApiDotNetCoreExample.Logic; namespace JsonApiDotNetCoreExample { @@ -45,7 +43,6 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services) mvcBuilder, discovery => discovery.AddCurrentAssembly()); - services.AddSingleton(); return services.BuildServiceProvider(); } diff --git a/src/JsonApiDotNetCore/Data/Article.cs b/src/JsonApiDotNetCore/Data/Article.cs new file mode 100644 index 0000000000..004d5d2f71 --- /dev/null +++ b/src/JsonApiDotNetCore/Data/Article.cs @@ -0,0 +1,4 @@ +namespace JsonApiDotNetCore.Data +{ + +} diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 3cff54f482..3b7c110b2d 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -2,15 +2,17 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; +using System.Reflection; using System.Threading.Tasks; using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Internal.Query; -using JsonApiDotNetCore.Logic; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.Extensions.Logging; @@ -24,18 +26,18 @@ public class DefaultEntityRepository { public DefaultEntityRepository( IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver, - ILogicCache logicCache = null) - : base(jsonApiContext, contextResolver, logicCache) + IDbContextResolver contextResolver + ) + : base(jsonApiContext, contextResolver) { } public DefaultEntityRepository( ILoggerFactory loggerFactory, IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver, - ILogicCache logicCache = null + IDbContextResolver contextResolver + ) - : base(loggerFactory, jsonApiContext, contextResolver, logicCache) + : base(loggerFactory, jsonApiContext, contextResolver) { } } @@ -53,42 +55,34 @@ public class DefaultEntityRepository private readonly ILogger _logger; private readonly IJsonApiContext _jsonApiContext; private readonly IGenericProcessorFactory _genericProcessorFactory; - private readonly ILogicCache _logicCache; private readonly ResourceDefinition _resourceDefinition; public DefaultEntityRepository( IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver, - ILogicCache logicCache) + IDbContextResolver contextResolver + ) { _context = contextResolver.GetContext(); _dbSet = contextResolver.GetDbSet(); _jsonApiContext = jsonApiContext; _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; - if (logicCache != null) - { - _logicCache = logicCache; - _resourceDefinition = (ResourceDefinition)logicCache.GetLogic(typeof(TEntity)); - } + } public DefaultEntityRepository( ILoggerFactory loggerFactory, IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver, - ILogicCache logicCache) + IDbContextResolver contextResolver + ) { _context = contextResolver.GetContext(); _dbSet = contextResolver.GetDbSet(); _jsonApiContext = jsonApiContext; _logger = loggerFactory.CreateLogger>(); _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; - if (logicCache != null) - { - _logicCache = logicCache; - _resourceDefinition = (ResourceDefinition)logicCache.GetLogic(typeof(TEntity)); - } + _resourceDefinition = _genericProcessorFactory.GetProcessor>(typeof(ResourceDefinition<>), typeof(TEntity)); + } /// @@ -367,51 +361,212 @@ public virtual IQueryable Include(IQueryable entities, string string internalRelationshipPath = null; var entity = _jsonApiContext.RequestEntity; - var logicTree = new Dictionary() { }; + IResourceDefinition logic; + + var requestedRelationship = relationshipChain[0]; + var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == requestedRelationship); + // need to add this to the typetree so we know what we're dealing with + if (relationship == null) + { + throw new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {entity.EntityName}", + $"{entity.EntityName} does not have a relationship named {requestedRelationship}"); + } + + logic = GetLogic(entity.EntityType); + + + if (relationship.CanInclude == false) + { + throw new JsonApiException(400, $"Including the relationship {requestedRelationship} on {entity.EntityName} is not allowed"); + } + + internalRelationshipPath = (internalRelationshipPath == null) ? relationship.RelationshipPath : $"{internalRelationshipPath}.{relationship.RelationshipPath}"; + - for (var i = 0; i < relationshipChain.Length; i++) + //entity = _jsonApiContext.ResourceGraph.GetContextEntity(relationship.Type); + + + // if we have logic, we should apply this when getting the relationship + entities = entities.Include(relationship.RelationshipPath); + //entites.ArticleTags.Tag -> (internalThroughName).InternalRelationshipname + if (relationship.IsHasMany) { - var requestedRelationship = relationshipChain[i]; - var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == requestedRelationship); + // we need to be nested + var children = GetChildren(entities as IIncludableQueryable, relationship, entity, relationshipChain); + } + return entities; + } - // need to add this to the typetree so we know what we're dealing with - var logic = _logicCache.GetLogic(relationship.Type); - if (logic != null) + + + + /// + /// we Are building the many-to-many relationship here + /// + /// + /// if many-to-many we directly go to the one in question: article -> articleTag -> tag (articleTag is skipped!) + /// + /// articles.Include(a => a.ArticleTags) + /// .ThenInclude(at => at.Tag) + /// + /// + /// + /// + /// + private IQueryable GetChildren( + IIncludableQueryable entities, + RelationshipAttribute relationship, + ContextEntity baseEntity, + string[] relationshipChain, + int depth = 0 + ) + { + var concreteType = typeof(TEntity); + ContextEntity entity; + // we are already getting tags here + var parameters = Expression.Parameter(typeof(TEntity), "model"); + ConstantExpression right; + + IIncludableQueryable temp; + if (relationship.GetType() == typeof(HasManyThroughAttribute)) //assume many-to-many + { + var castRelationship = (HasManyThroughAttribute)relationship; + if (depth > 0) { - logicTree.Add(relationship.Type, logic); + if (depth == 1) + { + // we are at the throughtable so {ArticleTag}, we need {Tag} + // we want to make articleTag => articleTag.Tag + + // {articleTag} + var parameterOne = Expression.Parameter(castRelationship.ThroughType, "articleTag"); + + // {articleTag.Tag} + var body = Expression.PropertyOrField(parameterOne, castRelationship.RightProperty.Name); + + // make expression for this one (with logic!) + // REFLECTION + var baseType = this.GetType(); + var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeThenInclude)); + var genericMethod = method.MakeGenericMethod(new[] { castRelationship.ThroughType, castRelationship.RightProperty.PropertyType }); + return genericMethod.Invoke(this, new object[] { entities, body, parameterOne }) as IIncludableQueryable; + + + } + + throw new NotImplementedException(); + } - if (relationship == null) + else { - throw new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {entity.EntityName}", - $"{entity.EntityName} does not have a relationship named {requestedRelationship}"); - } - if (relationship.CanInclude == false) - { - throw new JsonApiException(400, $"Including the relationship {requestedRelationship} on {entity.EntityName} is not allowed"); + var relationProperty = concreteType.GetProperty(castRelationship.InternalThroughName); + // {article} + var parameterOne = Expression.Parameter(typeof(TEntity), "article"); + if (relationProperty == null) + throw new ArgumentException($"'{castRelationship.InternalRelationshipName}' is not a valid relationship of '{concreteType}'"); + + var relatedType = relationship.Type; + + // {article.ArticleTags} + var left = Expression.PropertyOrField(parameterOne, castRelationship.InternalThroughName); + + // we arent doing anything with it, so just return the right side + var body = left; + // make expression for this one (with logic!) + //var includeExpression = Expression.Lambda>(body, parameterOne); + + + //temp = entities.Include(includeExpression); + + var baseType = this.GetType(); + var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeInclude)); + var genericMethod = method.MakeGenericMethod(new[] { castRelationship.RightProperty.PropertyType }); + temp = (IIncludableQueryable) genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); + + // we stil need to do some lovely little ThenIncluding + return GetChildren(temp, relationship, baseEntity, relationshipChain, depth: depth + 1); + } - internalRelationshipPath = (internalRelationshipPath == null) - ? relationship.RelationshipPath - : $"{internalRelationshipPath}.{relationship.RelationshipPath}"; - if (i < relationshipChain.Length) - entity = _jsonApiContext.ResourceGraph.GetContextEntity(relationship.Type); } + else + { + var castRelationship = (HasManyThroughAttribute)relationship; + var relationProperty = concreteType.GetProperty(castRelationship.InternalThroughName); + // {article} + var parameterOne = Expression.Parameter(typeof(TEntity), "article"); + if (relationProperty == null) + throw new ArgumentException($"'{castRelationship.InternalRelationshipName}' is not a valid relationship of '{concreteType}'"); + + var relatedType = relationship.Type; + + // {article.ArticleTags} + var left = Expression.PropertyOrField(parameterOne, castRelationship.InternalThroughName); + + // we arent doing anything with it, so just return the right side + var body = left; + + + + if (depth > 0) + { + // make expression for this one (with logic!) + var includeExpression = Expression.Lambda>(body, parameterOne); //yeah.. object,object + temp = entities as IIncludableQueryable; + temp.ThenInclude(includeExpression); + + } + } - // here we also need to check for any filters - // we have access to the logicTree which should have the - // Required logic to handle nested changes to the entities - // we need to - entities = entities.Include(internalRelationshipPath); - // I mean, the including is fine, but we still need to apply a `Where` somehow - // we could dynamically build - // entities.Where(e = > logic.Apply(e)) or something, but I cant really figure out how atm return entities; } + /// + /// Works. For now. + /// + /// + /// + /// + /// + /// + /// + /// + public IIncludableQueryable MakeThenInclude(IIncludableQueryable entities, MemberExpression body, ParameterExpression parameter) + { + var expression = Expression.Lambda>(body, parameter); + return entities.ThenInclude(expression); + } + public IIncludableQueryable MakeInclude(IQueryable entities, MemberExpression body, ParameterExpression parameter) + { + var expression = Expression.Lambda>(body, parameter); + return entities.Include(expression); + } + private IIncludableQueryable CustomThenInclude(IIncludableQueryable temp, Expression> includeExpression, Type from, Type to) where TType : class, IIdentifiable + { + Type baseType = temp.GetType(); + MethodInfo getMethod = baseType.GetMethod("ThenInclude", BindingFlags.Public); + MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { from, to }); + return (IIncludableQueryable)genericGet.Invoke(temp, new object[] { includeExpression }); + } + + private IQueryable CallLogic(IQueryable entities, IResourceDefinition resourceDefinition) where TType : class, IIdentifiable + { + Type resourceType = resourceDefinition.GetType(); + MethodInfo getMethod = resourceType.GetMethod("OnList", System.Reflection.BindingFlags.Public); + MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { typeof(TType) }); + + + return (IQueryable)genericGet.Invoke(resourceType, new object[] { entities }); + } + + private IResourceDefinition GetLogic(Type model) + { + return _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), model); + } diff --git a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs index af33e883a6..8d860fcf01 100644 --- a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs @@ -236,6 +236,14 @@ private static IQueryable CallGenericWhereContainsMethod(IQuer } } + /// + /// This calls a generic where method.. more explaining to follow + /// + /// + /// + /// + /// + /// private static IQueryable CallGenericWhereMethod(IQueryable source, BaseFilterQuery filter) { var op = filter.FilterOperation; diff --git a/src/JsonApiDotNetCore/Logic/ILogicCache.cs b/src/JsonApiDotNetCore/Logic/ILogicCache.cs deleted file mode 100644 index 32dfc36dc1..0000000000 --- a/src/JsonApiDotNetCore/Logic/ILogicCache.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Logic -{ - public interface ILogicCache - { - IResourceDefinition GetLogic(Type type); - } - public class BaseLogicCache : ILogicCache - { - protected Dictionary _cache = new Dictionary(); - - - public IResourceDefinition GetLogic(Type type) - { - var toReturn = _cache.FirstOrDefault(c => c.Key == type); - if(toReturn.Equals(default(KeyValuePair))) - { - return null; - } - return toReturn.Value; - } - } - -} diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 8c9a3c7aef..36927285fe 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -8,6 +8,10 @@ namespace JsonApiDotNetCore.Models { + public interface IResourceDefinition : IResourceDefinition where T : class, IIdentifiable + { + IQueryable OnList(IQueryable entities); + } public interface IResourceDefinition { List GetOutputAttrs(object instance); @@ -20,7 +24,7 @@ public interface IResourceDefinition /// service and repository layers. ///
/// The resource type - public class ResourceDefinition : IResourceDefinition where T : class, IIdentifiable + public class ResourceDefinition : IResourceDefinition where T : class, IIdentifiable { private readonly IResourceGraph _graph; private readonly ContextEntity _contextEntity; @@ -53,14 +57,13 @@ private bool InstanceOutputAttrsAreSpecified() /// /// Remove an attribute - /// - /// @TODO: need to investigate options for caching these /// /// the filter to execute /// @TODO /// protected List Remove(Expression> filter, List from = null) { + //@TODO: need to investigate options for caching these from = from ?? _contextEntity.Attributes; // model => model.Attribute @@ -172,6 +175,7 @@ private List GetOutputAttrs() public virtual IQueryable OnList(IQueryable entities) => entities; + /// /// This is an alias type intended to simplify the implementation's /// method signature. @@ -199,15 +203,15 @@ public class QueryFilters : Dictionary, string, IQuer internal List<(AttrAttribute, SortDirection)> DefaultSort() { var defaultSortOrder = GetDefaultSortOrder(); - if(defaultSortOrder != null && defaultSortOrder.Count > 0) + if (defaultSortOrder != null && defaultSortOrder.Count > 0) { var order = new List<(AttrAttribute, SortDirection)>(); - foreach(var sortProp in defaultSortOrder) + foreach (var sortProp in defaultSortOrder) { // TODO: error handling, log or throw? if (sortProp.Item1.Body is MemberExpression memberExpression) order.Add( - (_contextEntity.Attributes.SingleOrDefault(a => a.InternalAttributeName != memberExpression.Member.Name), + (_contextEntity.Attributes.SingleOrDefault(a => a.InternalAttributeName != memberExpression.Member.Name), sortProp.Item2) ); } @@ -218,6 +222,9 @@ public class QueryFilters : Dictionary, string, IQuer return null; } + + + /// /// This is an alias type intended to simplify the implementation's /// method signature. From b8e4b99491c5fc4ac46ecea6b8a6ff0b721ec7bf Mon Sep 17 00:00:00 2001 From: Harro van der Kroft Date: Tue, 26 Feb 2019 11:56:26 +0100 Subject: [PATCH 004/168] feat: added logic when retrieving information --- .../Resources/TagResource.cs | 6 +- .../Resources/UserResource.cs | 2 +- .../Data/DefaultEntityRepository.cs | 104 ++++++++++++++++-- .../Data/IEntityRepository.cs | 6 +- .../Models/ResourceDefinition.cs | 10 +- .../Services/EntityResourceService.cs | 43 +++++++- .../ResourceDefinitionTests.cs | 20 +++- 7 files changed, 161 insertions(+), 30 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs index 872487218c..1937629e7b 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -8,6 +8,10 @@ namespace JsonApiDotNetCoreExample.Resources public class TagResource : ResourceDefinition { - public override IQueryable OnList(IQueryable entities) => entities.Where(t => t.Name != "THISTAGSHOULDNOTBEVISIBLE"); + public override List OnList(List entities) + { + return entities.Where(t => t.Name != "THISTAGSHOULDNOTBEVISIBLE").ToList(); + } + } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index eca5e6ce44..06016539d6 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -10,6 +10,6 @@ public class UserResource : ResourceDefinition protected override List OutputAttrs() => Remove(user => user.Password); - public override IQueryable OnList(IQueryable entities) => entities; + public override List OnList(List entities) => entities; } } diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 3b7c110b2d..ff594932ad 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -15,7 +15,7 @@ using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.Extensions.Logging; - +using System.Linq; namespace JsonApiDotNetCore.Data { /// @@ -89,10 +89,8 @@ IDbContextResolver contextResolver public virtual IQueryable Get() { var entities = (IQueryable)_dbSet; - if (_resourceDefinition != null) - { - entities = _resourceDefinition.OnList(entities); - } + + if (_jsonApiContext.QuerySet?.Fields != null && _jsonApiContext.QuerySet.Fields.Count > 0) return entities.Select(_jsonApiContext.QuerySet?.Fields); @@ -100,6 +98,8 @@ public virtual IQueryable Get() return entities; } + + /// public virtual IQueryable Filter(IQueryable entities, FilterQuery filterQuery) { @@ -140,6 +140,7 @@ public virtual IQueryable Sort(IQueryable entities, List public virtual async Task GetAsync(TId id) { + return await Get().SingleOrDefaultAsync(e => e.Id.Equals(id)); } @@ -387,12 +388,12 @@ public virtual IQueryable Include(IQueryable entities, string // if we have logic, we should apply this when getting the relationship - entities = entities.Include(relationship.RelationshipPath); + //entities = entities.Include(relationship.RelationshipPath); //entites.ArticleTags.Tag -> (internalThroughName).InternalRelationshipname if (relationship.IsHasMany) { // we need to be nested - var children = GetChildren(entities as IIncludableQueryable, relationship, entity, relationshipChain); + entities = GetChildren(entities, relationship, entity, relationshipChain); } return entities; } @@ -414,10 +415,11 @@ public virtual IQueryable Include(IQueryable entities, string /// /// private IQueryable GetChildren( - IIncludableQueryable entities, + IQueryable entities, RelationshipAttribute relationship, ContextEntity baseEntity, string[] relationshipChain, + string viewKind = "list", int depth = 0 ) { @@ -444,14 +446,21 @@ private IQueryable GetChildren( // {articleTag.Tag} var body = Expression.PropertyOrField(parameterOne, castRelationship.RightProperty.Name); + var logic = GetLogic(castRelationship.RightProperty.PropertyType); + // make expression for this one (with logic!) // REFLECTION + // get logic + var baseType = this.GetType(); - var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeThenInclude)); + var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeThenIncludeList)); var genericMethod = method.MakeGenericMethod(new[] { castRelationship.ThroughType, castRelationship.RightProperty.PropertyType }); - return genericMethod.Invoke(this, new object[] { entities, body, parameterOne }) as IIncludableQueryable; + var tempEntities = genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); + + // apply logic + return tempEntities as IIncludableQueryable; } throw new NotImplementedException(); @@ -481,7 +490,7 @@ private IQueryable GetChildren( var baseType = this.GetType(); var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeInclude)); - var genericMethod = method.MakeGenericMethod(new[] { castRelationship.RightProperty.PropertyType }); + var genericMethod = method.MakeGenericMethod(new[] { castRelationship.ThroughProperty.PropertyType }); temp = (IIncludableQueryable) genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); // we stil need to do some lovely little ThenIncluding @@ -524,6 +533,76 @@ private IQueryable GetChildren( return entities; } + + + public IList ApplyLogic(IList entities, string rel) + { + // seeing as the relationships are already processed, we can just do + // Logic.{method}(articles.Tags) + + IResourceDefinition logic; + var entity = _jsonApiContext.RequestEntity; + + var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == rel); + Type l1 = typeof(List<>); + if(relationship.GetType() == typeof(HasManyThroughAttribute)) + { + var castRelationship = relationship as HasManyThroughAttribute; + object nestedLogic; + for (int index = 0; index < entities.Count(); ++index) { + // this is our {Article} + var listEntity = entities[index]; + // get articleTags + + var relevantProperty = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); + + var intermediateEntities = relevantProperty.GetValue(listEntity) as IEnumerable; + nestedLogic = GetLogic(castRelationship.Type); + var method = nestedLogic.GetType().GetMethods().First(e => e.Name == "OnList"); + // get Tags + // this can be replaced with some SelectMany's + Type constructed = l1.MakeGenericType(castRelationship.RightProperty.PropertyType); + object toHold = Activator.CreateInstance(constructed); + foreach (var iEntity in intermediateEntities) + { + //iterating over the ArticleTags + // we are getting all the Tag's + var tempArticleTags = (iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance).GetValue(iEntity)); + (toHold as IList).Add( tempArticleTags); + } + + toHold = method.Invoke(nestedLogic, new object[] { toHold }); + PropertyInfo prop = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); + if(null != prop && prop.CanWrite) + { + prop.SetValue(listEntity, toHold, null); + } + + + entities[index] = listEntity; + + } + + + } + return entities; + + } + + + + private static List ConvertList(List value, Type type) + { + return new List(value.Select(item => (T) Convert.ChangeType(item, type))); + } + + + + public IIncludableQueryable MakeThenIncludeList(IIncludableQueryable> entities, MemberExpression body, ParameterExpression parameter) + { + var expression = Expression.Lambda>(body, parameter); + return entities.ThenInclude(expression); + } /// /// Works. For now. /// @@ -545,6 +624,7 @@ public IIncludableQueryable MakeInclude(IQueryabl return entities.Include(expression); } + private IIncludableQueryable CustomThenInclude(IIncludableQueryable temp, Expression> includeExpression, Type from, Type to) where TType : class, IIdentifiable { Type baseType = temp.GetType(); @@ -614,5 +694,7 @@ public async Task> ToListAsync(IQueryable entiti ? await entities.ToListAsync() : entities.ToList(); } + + } } diff --git a/src/JsonApiDotNetCore/Data/IEntityRepository.cs b/src/JsonApiDotNetCore/Data/IEntityRepository.cs index ac69f4fdac..1fb181c723 100644 --- a/src/JsonApiDotNetCore/Data/IEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/IEntityRepository.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using System.Linq; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Data @@ -11,7 +13,9 @@ public interface IEntityRepository : IEntityReadRepository, IEntityWriteRepository where TEntity : class, IIdentifiable - { } + { + IList ApplyLogic(IList entities, string r); + } /// /// A staging interface to avoid breaking changes that diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 36927285fe..48537f401b 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -8,13 +8,11 @@ namespace JsonApiDotNetCore.Models { - public interface IResourceDefinition : IResourceDefinition where T : class, IIdentifiable - { - IQueryable OnList(IQueryable entities); - } + public interface IResourceDefinition { List GetOutputAttrs(object instance); + } /// @@ -24,7 +22,7 @@ public interface IResourceDefinition /// service and repository layers. /// /// The resource type - public class ResourceDefinition : IResourceDefinition where T : class, IIdentifiable + public class ResourceDefinition : IResourceDefinition where T : class, IIdentifiable { private readonly IResourceGraph _graph; private readonly ContextEntity _contextEntity; @@ -172,7 +170,7 @@ private List GetOutputAttrs() /// /// /// - public virtual IQueryable OnList(IQueryable entities) => entities; + public virtual List OnList(List entities) => entities; diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 53cfc7a7c1..787c80cdd2 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -1,5 +1,6 @@ using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; using Microsoft.Extensions.Logging; using System; @@ -39,6 +40,7 @@ public class EntityResourceService : where TEntity : class, IIdentifiable { private readonly IJsonApiContext _jsonApiContext; + private readonly IGenericProcessorFactory _genericProcessorFactory; private readonly IEntityRepository _entities; private readonly ILogger _logger; private readonly IResourceMapper _mapper; @@ -55,6 +57,7 @@ public EntityResourceService( } _jsonApiContext = jsonApiContext; + _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; _entities = entityRepository; _logger = loggerFactory?.CreateLogger>(); } @@ -82,7 +85,7 @@ public virtual async Task CreateAsync(TResource resource) // https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/343 if (ShouldIncludeRelationships()) { - if(_entities is IEntityFrameworkRepository efRepository) + if (_entities is IEntityFrameworkRepository efRepository) efRepository.DetachRelationshipPointers(entity); return await GetWithRelationshipsAsync(entity.Id); @@ -100,19 +103,40 @@ public virtual async Task> GetAsync() { var entities = _entities.Get(); + + entities = ApplySortAndFilterQuery(entities); if (ShouldIncludeRelationships()) + { + entities = IncludeRelationships(entities, _jsonApiContext.QuerySet.IncludedRelationships); + } + if (_jsonApiContext.Options.IncludeTotalRecordCount) _jsonApiContext.PageManager.TotalRecords = await _entities.CountAsync(entities); - // pagination should be done last since it will execute the query - var pagedEntities = await ApplyPageQueryAsync(entities); + + IEnumerable pagedEntities; + // just to see if it works + if (ShouldIncludeRelationships()) + { + pagedEntities = ApplyNestedFilters(entities.ToList(), _jsonApiContext.QuerySet.IncludedRelationships) as IEnumerable; + // no pagination atm + } + else + { + // pagination should be done last since it will execute the query + pagedEntities = await ApplyPageQueryAsync(entities); + } + + return pagedEntities; } + + public virtual async Task GetAsync(TId id) { if (ShouldIncludeRelationships()) @@ -211,7 +235,18 @@ protected virtual async Task> ApplyPageQueryAsync(IQuerya return MapOut(pagedEntities); } - + private IEnumerable ApplyNestedFilters(IEnumerable entities, List relationships) + { + foreach(var r in relationships) + { + entities = _entities.ApplyLogic(entities.ToList(), r); + } + return entities; + } + private IResourceDefinition GetLogic(IQueryable models) + { + return _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), models.GetType()); + } protected virtual IQueryable ApplySortAndFilterQuery(IQueryable entities) { var query = _jsonApiContext.QuerySet; diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs index c93f7f192b..1825962d77 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs @@ -41,19 +41,27 @@ public async Task Tag_Is_Hidden() // Arrange var context = _fixture.GetService(); var article = _articleFaker.Generate(); - var tag = _tagFaker.Generate(); + var tags = _tagFaker.Generate(2); - tag.Name = "THISTAGSHOULDNOTBEVISIBLE"; + tags[0].Name = "THISTAGSHOULDNOTBEVISIBLE"; context.Articles.RemoveRange(context.Articles); await context.SaveChangesAsync(); - var articleTag = new ArticleTag + var articleTags = new ArticleTag[] { - Article = article, - Tag = tag + new ArticleTag + { + Article = article, + Tag = tags[0] + }, + new ArticleTag + { + Article = article, + Tag = tags[1] + } }; - context.ArticleTags.Add(articleTag); + context.ArticleTags.AddRange(articleTags); await context.SaveChangesAsync(); var route = $"/api/v1/articles?include=tags"; From 128fbc4c3497c0300962044cd7a4b77da5f3cb9a Mon Sep 17 00:00:00 2001 From: Harro van der Kroft Date: Fri, 22 Mar 2019 17:51:33 +0100 Subject: [PATCH 005/168] feat: first ever working test on resourcedefinition --- .../Resources/TagResource.cs | 4 +- .../Resources/UserResource.cs | 2 +- .../Data/DefaultEntityRepository.cs | 136 ++++++++++++------ .../Models/ResourceDefinition.cs | 2 +- .../Services/EntityResourceService.cs | 23 ++- .../ResourceDefinitionTests.cs | 22 ++- 6 files changed, 124 insertions(+), 65 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs index 1937629e7b..e732803a77 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -7,11 +7,9 @@ namespace JsonApiDotNetCoreExample.Resources { public class TagResource : ResourceDefinition { - - public override List OnList(List entities) + public override IEnumerable OnList(List entities) { return entities.Where(t => t.Name != "THISTAGSHOULDNOTBEVISIBLE").ToList(); } - } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index 06016539d6..a408dc3a4a 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -10,6 +10,6 @@ public class UserResource : ResourceDefinition protected override List OutputAttrs() => Remove(user => user.Password); - public override List OnList(List entities) => entities; + public override IEnumerable OnList(List entities) => entities; } } diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index ff594932ad..0d5ba3f39e 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -66,8 +66,6 @@ IDbContextResolver contextResolver _dbSet = contextResolver.GetDbSet(); _jsonApiContext = jsonApiContext; _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; - - } public DefaultEntityRepository( @@ -82,24 +80,18 @@ IDbContextResolver contextResolver _logger = loggerFactory.CreateLogger>(); _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; _resourceDefinition = _genericProcessorFactory.GetProcessor>(typeof(ResourceDefinition<>), typeof(TEntity)); - } /// public virtual IQueryable Get() { var entities = (IQueryable)_dbSet; - - - if (_jsonApiContext.QuerySet?.Fields != null && _jsonApiContext.QuerySet.Fields.Count > 0) return entities.Select(_jsonApiContext.QuerySet?.Fields); return entities; } - - /// public virtual IQueryable Filter(IQueryable entities, FilterQuery filterQuery) { @@ -111,7 +103,6 @@ public virtual IQueryable Filter(IQueryable entities, FilterQu return defaultQueryFilter(entities, filterQuery.Value); } } - return entities.Filter(_jsonApiContext, filterQuery); } @@ -359,11 +350,10 @@ public virtual IQueryable Include(IQueryable entities, string // variables mutated in recursive loop // TODO: make recursive method - string internalRelationshipPath = null; - var entity = _jsonApiContext.RequestEntity; IResourceDefinition logic; - + string internalRelationshipPath = null; + var entity = _jsonApiContext.RequestEntity; var requestedRelationship = relationshipChain[0]; var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == requestedRelationship); // need to add this to the typetree so we know what we're dealing with @@ -394,12 +384,18 @@ public virtual IQueryable Include(IQueryable entities, string { // we need to be nested entities = GetChildren(entities, relationship, entity, relationshipChain); + // lets get this query fully done + // get tags + var entitiesList = ApplyLogic(entities.ToList(), "tags"); + //// we need it to be an IENumerable for a second so lets do that + //var tempEnumerable = entities.ToList(); + //var logicMethod = rightLogic.GetType().GetRuntimeMethods().First(e => e.Name == "OnList"); + //var tempEntities = logicMethod.Invoke(this, new object[] { entities.ToList() }) as IQueryable; } - return entities; - } - + return entities; + } /// /// we Are building the many-to-many relationship here @@ -446,19 +442,17 @@ private IQueryable GetChildren( // {articleTag.Tag} var body = Expression.PropertyOrField(parameterOne, castRelationship.RightProperty.Name); - var logic = GetLogic(castRelationship.RightProperty.PropertyType); + // make expression for this one (with logic!) // REFLECTION // get logic - var baseType = this.GetType(); var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeThenIncludeList)); var genericMethod = method.MakeGenericMethod(new[] { castRelationship.ThroughType, castRelationship.RightProperty.PropertyType }); var tempEntities = genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); - // apply logic return tempEntities as IIncludableQueryable; } @@ -487,38 +481,37 @@ private IQueryable GetChildren( //temp = entities.Include(includeExpression); - + // This is the same as + // temp = entities.Include(includeExpression); var baseType = this.GetType(); var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeInclude)); var genericMethod = method.MakeGenericMethod(new[] { castRelationship.ThroughProperty.PropertyType }); - temp = (IIncludableQueryable) genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); + temp = (IIncludableQueryable)genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); + // Sow heave included {article.articeTags.tags} + //var logic = GetLogic(entity.EntityType) // we stil need to do some lovely little ThenIncluding return GetChildren(temp, relationship, baseEntity, relationshipChain, depth: depth + 1); - } } else { - var castRelationship = (HasManyThroughAttribute)relationship; - var relationProperty = concreteType.GetProperty(castRelationship.InternalThroughName); + var castRelationship = (HasManyAttribute)relationship; + var relationProperty = concreteType.GetProperty(castRelationship.RelationshipPath); // {article} - var parameterOne = Expression.Parameter(typeof(TEntity), "article"); + var parameterOne = Expression.Parameter(typeof(TEntity), concreteType.ToString()); if (relationProperty == null) throw new ArgumentException($"'{castRelationship.InternalRelationshipName}' is not a valid relationship of '{concreteType}'"); var relatedType = relationship.Type; // {article.ArticleTags} - var left = Expression.PropertyOrField(parameterOne, castRelationship.InternalThroughName); + var left = Expression.PropertyOrField(parameterOne, castRelationship.RelationshipPath); // we arent doing anything with it, so just return the right side var body = left; - - - if (depth > 0) { // make expression for this one (with logic!) @@ -527,7 +520,13 @@ private IQueryable GetChildren( temp = entities as IIncludableQueryable; temp.ThenInclude(includeExpression); - + } + else + { + var baseType = GetType(); + var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeIncludeList)); + var genericMethod = method.MakeGenericMethod(new[] { castRelationship.Type }); + temp = (IIncludableQueryable)genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); } } @@ -535,6 +534,7 @@ private IQueryable GetChildren( } + public IList ApplyLogic(IList entities, string rel) { // seeing as the relationships are already processed, we can just do @@ -545,37 +545,71 @@ public IList ApplyLogic(IList entities, string rel) var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == rel); Type l1 = typeof(List<>); - if(relationship.GetType() == typeof(HasManyThroughAttribute)) + if (relationship.GetType() == typeof(HasManyThroughAttribute)) { var castRelationship = relationship as HasManyThroughAttribute; object nestedLogic; - for (int index = 0; index < entities.Count(); ++index) { + for (int index = 0; index < entities.Count(); ++index) + { // this is our {Article} var listEntity = entities[index]; - // get articleTags - + // Get the {Article.ArticleTags} var relevantProperty = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); + var intermediateEntities = relevantProperty.GetValue(listEntity) as IList; - var intermediateEntities = relevantProperty.GetValue(listEntity) as IEnumerable; + // Logic for this nested property nestedLogic = GetLogic(castRelationship.Type); + + // We default to OnList at the moment var method = nestedLogic.GetType().GetMethods().First(e => e.Name == "OnList"); - // get Tags - // this can be replaced with some SelectMany's + + // get Tags, this can be replaced with some SelectMany's Type constructed = l1.MakeGenericType(castRelationship.RightProperty.PropertyType); - object toHold = Activator.CreateInstance(constructed); + + // Not happy with this, but was needed. + IList toHold = Activator.CreateInstance(constructed) as IList; foreach (var iEntity in intermediateEntities) { - //iterating over the ArticleTags - // we are getting all the Tag's - var tempArticleTags = (iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance).GetValue(iEntity)); - (toHold as IList).Add( tempArticleTags); + // Iterating over the {ArticleTag}s to get all the {Tag}s + var fetchedTags = (iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance).GetValue(iEntity)); + toHold.Add(fetchedTags); } + // + + IEnumerable filteredTags = method.Invoke(nestedLogic, new object[] { toHold }) as IEnumerable; + toHold = filteredTags as IList; - toHold = method.Invoke(nestedLogic, new object[] { toHold }); + var toHoldHashSet = (HashSet) GetHashSet((IEnumerable)filteredTags); + // We no process all {Article.ArticleTags} in a for loop because we're changing it + for (int i = 0; i < intermediateEntities.Count; i++) + { + var iEntity = intermediateEntities[i]; + var property = iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance); + + var item = property.GetValue(iEntity); + + if (!toHoldHashSet.ToList().Contains(item)) + { + // What we first did: + //property.SetValue(iEntity, null); + // if {tag} is not filled, we shouldnt fill in {ArticleTag} because otherwise + // JsonApiDotNetCore will try to show a null value, resulting in object refrence not set to an instance of an object + // so we delete it here. + // We shouldnt remove, I'm just emulating the WHERE here... + intermediateEntities.RemoveAt(i); + } + else + { + // dont need to do anything + // Maybe, in the future, when patches are done we need to do + // this: + // intermediateEntities[i] = iEntity; + } + } PropertyInfo prop = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); - if(null != prop && prop.CanWrite) + if (null != prop && prop.CanWrite) { - prop.SetValue(listEntity, toHold, null); + prop.SetValue(listEntity, intermediateEntities); } @@ -588,12 +622,15 @@ public IList ApplyLogic(IList entities, string rel) return entities; } - + public HashSet GetHashSet(IEnumerable source) + { + return new HashSet(source); + } private static List ConvertList(List value, Type type) { - return new List(value.Select(item => (T) Convert.ChangeType(item, type))); + return new List(value.Select(item => (T)Convert.ChangeType(item, type))); } @@ -603,6 +640,7 @@ public IIncludableQueryable MakeThenIncludeList>(body, parameter); return entities.ThenInclude(expression); } + /// /// Works. For now. /// @@ -623,7 +661,11 @@ public IIncludableQueryable MakeInclude(IQueryabl var expression = Expression.Lambda>(body, parameter); return entities.Include(expression); } - + public IIncludableQueryable MakeIncludeList(IQueryable entities, MemberExpression body, ParameterExpression parameter) + { + var expression = Expression.Lambda>>(body, parameter); + return (IIncludableQueryable < TEntity, TProperty >) entities.Include(expression); + } private IIncludableQueryable CustomThenInclude(IIncludableQueryable temp, Expression> includeExpression, Type from, Type to) where TType : class, IIdentifiable { diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 48537f401b..e268f8e08d 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -170,7 +170,7 @@ private List GetOutputAttrs() /// /// /// - public virtual List OnList(List entities) => entities; + public virtual IEnumerable OnList(List entities) => entities; diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 787c80cdd2..1e948f4a76 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -120,17 +120,16 @@ public virtual async Task> GetAsync() IEnumerable pagedEntities; // just to see if it works - if (ShouldIncludeRelationships()) - { - pagedEntities = ApplyNestedFilters(entities.ToList(), _jsonApiContext.QuerySet.IncludedRelationships) as IEnumerable; - // no pagination atm - } - else - { - // pagination should be done last since it will execute the query - pagedEntities = await ApplyPageQueryAsync(entities); - } - + //if (ShouldIncludeRelationships()) + //{ + // pagedEntities = ApplyNestedFilters(entities.ToList(), _jsonApiContext.QuerySet.IncludedRelationships) as IEnumerable; + // // no pagination atm + //} + //else + //{ + // pagination should be done last since it will execute the query + pagedEntities = await ApplyPageQueryAsync(entities); + //} return pagedEntities; } @@ -237,7 +236,7 @@ protected virtual async Task> ApplyPageQueryAsync(IQuerya } private IEnumerable ApplyNestedFilters(IEnumerable entities, List relationships) { - foreach(var r in relationships) + foreach (var r in relationships) { entities = _entities.ApplyLogic(entities.ToList(), r); } diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs index 1825962d77..1bb665e523 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Net; @@ -15,6 +16,24 @@ namespace JsonApiDotNetCoreExampleTests.Acceptance { + public class AssertHelper + { + public static void HasEqualFieldValues(T expected, T actual) + { + var failures = new List(); + var fields = typeof(T).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + foreach (var field in fields) + { + var v1 = field.GetValue(expected); + var v2 = field.GetValue(actual); + if (v1 == null && v2 == null) continue; + if (!v1.Equals(v2)) failures.Add(string.Format("{0}: Expected:<{1}> Actual:<{2}>", field.Name, v1, v2)); + } + if (failures.Any()) + Assert.True(false, "AssertHelper.HasEqualFieldValues failed. " + Environment.NewLine + string.Join(Environment.NewLine, failures)); + } + } + [Collection("WebHostCollection")] public class ResourceDefinitionTests { @@ -87,7 +106,8 @@ public async Task Tag_Is_Hidden() Assert.NotNull(articleResponse); Assert.Equal(article.Name, articleResponse.Name); - Assert.Null(articleResponse.Tags); + + AssertHelper.HasEqualFieldValues(articleResponse.Tags[0], tags[1]); } From c6f94bb73b8eac9aebf95a7ad84c9247cffa12e2 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 4 Apr 2019 10:58:32 +0200 Subject: [PATCH 006/168] chore: cleanup of unused functions, restored original usage of Include method as we are no longer going to build expression trees manually --- .../Data/DefaultEntityRepository.cs | 66 ++++++------------- .../Data/IEntityRepository.cs | 2 +- src/JsonApiDotNetCore/JsonApiDotNetCore.sln | 17 +++++ .../Services/EntityResourceService.cs | 40 +---------- 4 files changed, 42 insertions(+), 83 deletions(-) create mode 100644 src/JsonApiDotNetCore/JsonApiDotNetCore.sln diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 0d5ba3f39e..de837ebdac 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -71,7 +71,8 @@ IDbContextResolver contextResolver public DefaultEntityRepository( ILoggerFactory loggerFactory, IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver + IDbContextResolver contextResolver, + ResourceDefinition resourceDefinition = null ) { _context = contextResolver.GetContext(); @@ -79,7 +80,7 @@ IDbContextResolver contextResolver _jsonApiContext = jsonApiContext; _logger = loggerFactory.CreateLogger>(); _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; - _resourceDefinition = _genericProcessorFactory.GetProcessor>(typeof(ResourceDefinition<>), typeof(TEntity)); + _resourceDefinition = resourceDefinition; } /// @@ -350,51 +351,32 @@ public virtual IQueryable Include(IQueryable entities, string // variables mutated in recursive loop // TODO: make recursive method - - IResourceDefinition logic; string internalRelationshipPath = null; var entity = _jsonApiContext.RequestEntity; - var requestedRelationship = relationshipChain[0]; - var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == requestedRelationship); - // need to add this to the typetree so we know what we're dealing with - if (relationship == null) - { - throw new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {entity.EntityName}", - $"{entity.EntityName} does not have a relationship named {requestedRelationship}"); - } - - logic = GetLogic(entity.EntityType); - - - if (relationship.CanInclude == false) + for (var i = 0; i < relationshipChain.Length; i++) { - throw new JsonApiException(400, $"Including the relationship {requestedRelationship} on {entity.EntityName} is not allowed"); - } - - internalRelationshipPath = (internalRelationshipPath == null) ? relationship.RelationshipPath : $"{internalRelationshipPath}.{relationship.RelationshipPath}"; - + var requestedRelationship = relationshipChain[i]; + var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == requestedRelationship); + if (relationship == null) + { + throw new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {entity.EntityName}", + $"{entity.EntityName} does not have a relationship named {requestedRelationship}"); + } - //entity = _jsonApiContext.ResourceGraph.GetContextEntity(relationship.Type); + if (relationship.CanInclude == false) + { + throw new JsonApiException(400, $"Including the relationship {requestedRelationship} on {entity.EntityName} is not allowed"); + } + internalRelationshipPath = (internalRelationshipPath == null) + ? relationship.RelationshipPath + : $"{internalRelationshipPath}.{relationship.RelationshipPath}"; - // if we have logic, we should apply this when getting the relationship - //entities = entities.Include(relationship.RelationshipPath); - //entites.ArticleTags.Tag -> (internalThroughName).InternalRelationshipname - if (relationship.IsHasMany) - { - // we need to be nested - entities = GetChildren(entities, relationship, entity, relationshipChain); - // lets get this query fully done - // get tags - var entitiesList = ApplyLogic(entities.ToList(), "tags"); - //// we need it to be an IENumerable for a second so lets do that - //var tempEnumerable = entities.ToList(); - //var logicMethod = rightLogic.GetType().GetRuntimeMethods().First(e => e.Name == "OnList"); - //var tempEntities = logicMethod.Invoke(this, new object[] { entities.ToList() }) as IQueryable; + if (i < relationshipChain.Length) + entity = _jsonApiContext.ResourceGraph.GetContextEntity(relationship.Type); } - - return entities; + return entities.Include(internalRelationshipPath); } /// @@ -626,21 +608,15 @@ public HashSet GetHashSet(IEnumerable source) { return new HashSet(source); } - - private static List ConvertList(List value, Type type) { return new List(value.Select(item => (T)Convert.ChangeType(item, type))); } - - - public IIncludableQueryable MakeThenIncludeList(IIncludableQueryable> entities, MemberExpression body, ParameterExpression parameter) { var expression = Expression.Lambda>(body, parameter); return entities.ThenInclude(expression); } - /// /// Works. For now. /// diff --git a/src/JsonApiDotNetCore/Data/IEntityRepository.cs b/src/JsonApiDotNetCore/Data/IEntityRepository.cs index 1fb181c723..9a60d813bc 100644 --- a/src/JsonApiDotNetCore/Data/IEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/IEntityRepository.cs @@ -14,7 +14,7 @@ public interface IEntityRepository IEntityWriteRepository where TEntity : class, IIdentifiable { - IList ApplyLogic(IList entities, string r); + //IList ApplyLogic(IList entities, string r); } /// diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.sln b/src/JsonApiDotNetCore/JsonApiDotNetCore.sln new file mode 100644 index 0000000000..58b08eb568 --- /dev/null +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.sln @@ -0,0 +1,17 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore", "JsonApiDotNetCore.csproj", "{C8E2AE2E-80E2-408E-89FF-F66BA720F879}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|Any CPU = Release|Any CPU + Debug|Any CPU = Debug|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C8E2AE2E-80E2-408E-89FF-F66BA720F879}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8E2AE2E-80E2-408E-89FF-F66BA720F879}.Release|Any CPU.Build.0 = Release|Any CPU + {C8E2AE2E-80E2-408E-89FF-F66BA720F879}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8E2AE2E-80E2-408E-89FF-F66BA720F879}.Debug|Any CPU.Build.0 = Debug|Any CPU + EndGlobalSection +EndGlobal diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 1e948f4a76..6876748bb4 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; using Microsoft.Extensions.Logging; using System; @@ -40,7 +39,6 @@ public class EntityResourceService : where TEntity : class, IIdentifiable { private readonly IJsonApiContext _jsonApiContext; - private readonly IGenericProcessorFactory _genericProcessorFactory; private readonly IEntityRepository _entities; private readonly ILogger _logger; private readonly IResourceMapper _mapper; @@ -57,7 +55,6 @@ public EntityResourceService( } _jsonApiContext = jsonApiContext; - _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; _entities = entityRepository; _logger = loggerFactory?.CreateLogger>(); } @@ -103,39 +100,19 @@ public virtual async Task> GetAsync() { var entities = _entities.Get(); - - entities = ApplySortAndFilterQuery(entities); if (ShouldIncludeRelationships()) - { - entities = IncludeRelationships(entities, _jsonApiContext.QuerySet.IncludedRelationships); - } - if (_jsonApiContext.Options.IncludeTotalRecordCount) _jsonApiContext.PageManager.TotalRecords = await _entities.CountAsync(entities); - - IEnumerable pagedEntities; - // just to see if it works - //if (ShouldIncludeRelationships()) - //{ - // pagedEntities = ApplyNestedFilters(entities.ToList(), _jsonApiContext.QuerySet.IncludedRelationships) as IEnumerable; - // // no pagination atm - //} - //else - //{ // pagination should be done last since it will execute the query - pagedEntities = await ApplyPageQueryAsync(entities); - //} - + var pagedEntities = await ApplyPageQueryAsync(entities); return pagedEntities; } - - public virtual async Task GetAsync(TId id) { if (ShouldIncludeRelationships()) @@ -234,18 +211,7 @@ protected virtual async Task> ApplyPageQueryAsync(IQuerya return MapOut(pagedEntities); } - private IEnumerable ApplyNestedFilters(IEnumerable entities, List relationships) - { - foreach (var r in relationships) - { - entities = _entities.ApplyLogic(entities.ToList(), r); - } - return entities; - } - private IResourceDefinition GetLogic(IQueryable models) - { - return _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), models.GetType()); - } + protected virtual IQueryable ApplySortAndFilterQuery(IQueryable entities) { var query = _jsonApiContext.QuerySet; @@ -310,4 +276,4 @@ private TEntity MapIn(TResource resource) ? resource as TEntity : _mapper.Map(resource); } -} +} \ No newline at end of file From 46ba26de48fcb9a58e7acd9fce8e6d54851531e5 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 4 Apr 2019 15:27:10 +0200 Subject: [PATCH 007/168] feat: abstracted resource definition logic into ResourceLogicExecutor, wired it up to many to many usecase --- .../Data/DefaultEntityRepository.cs | 322 ++---------------- .../Data/IEntityRepository.cs | 2 +- .../IServiceCollectionExtensions.cs | 2 +- .../Services/EntityResourceService.cs | 1 + .../Services/IResourceLogicExecutor.cs | 9 + .../Services/ResourceLogicExecutor.cs | 146 ++++++++ .../ResourceDefinitionTests.cs | 6 +- 7 files changed, 182 insertions(+), 306 deletions(-) create mode 100644 src/JsonApiDotNetCore/Services/IResourceLogicExecutor.cs create mode 100644 src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index de837ebdac..2db07a01e2 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -2,8 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; -using System.Reflection; using System.Threading.Tasks; using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; @@ -12,10 +10,7 @@ using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Query; -using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.Extensions.Logging; -using System.Linq; namespace JsonApiDotNetCore.Data { /// @@ -26,18 +21,19 @@ public class DefaultEntityRepository { public DefaultEntityRepository( IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver + IDbContextResolver contextResolver, + IResourceLogicExecutor logicExecutor = null ) - : base(jsonApiContext, contextResolver) + : base(jsonApiContext, contextResolver, logicExecutor) { } public DefaultEntityRepository( ILoggerFactory loggerFactory, IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver - + IDbContextResolver contextResolver, + IResourceLogicExecutor logicExecutor = null ) - : base(loggerFactory, jsonApiContext, contextResolver) + : base(loggerFactory, jsonApiContext, contextResolver, logicExecutor) { } } @@ -56,22 +52,26 @@ public class DefaultEntityRepository private readonly IJsonApiContext _jsonApiContext; private readonly IGenericProcessorFactory _genericProcessorFactory; private readonly ResourceDefinition _resourceDefinition; + private readonly IResourceLogicExecutor _logicExecutor; public DefaultEntityRepository( IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver + IDbContextResolver contextResolver, + IResourceLogicExecutor logicExecutor = null ) { _context = contextResolver.GetContext(); _dbSet = contextResolver.GetDbSet(); _jsonApiContext = jsonApiContext; _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; + _logicExecutor = logicExecutor; } public DefaultEntityRepository( ILoggerFactory loggerFactory, IJsonApiContext jsonApiContext, IDbContextResolver contextResolver, + IResourceLogicExecutor logicExecutor = null, ResourceDefinition resourceDefinition = null ) { @@ -81,6 +81,7 @@ public DefaultEntityRepository( _logger = loggerFactory.CreateLogger>(); _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; _resourceDefinition = resourceDefinition; + _logicExecutor = logicExecutor; } /// @@ -164,6 +165,12 @@ protected virtual void AttachRelationships(TEntity entity = null) AttachHasOnePointers(entity); } + public virtual IQueryable ApplyResourceDefinitionLogic(IQueryable entities, string rel) + { + if (_logicExecutor == null) return entities; + return _logicExecutor.ApplyLogic(entities.ToList(), rel).AsQueryable(); + } + /// public void DetachRelationshipPointers(TEntity entity) { @@ -379,301 +386,14 @@ public virtual IQueryable Include(IQueryable entities, string return entities.Include(internalRelationshipPath); } - /// - /// we Are building the many-to-many relationship here - /// - /// - /// if many-to-many we directly go to the one in question: article -> articleTag -> tag (articleTag is skipped!) - /// - /// articles.Include(a => a.ArticleTags) - /// .ThenInclude(at => at.Tag) - /// - /// - /// - /// - /// - private IQueryable GetChildren( - IQueryable entities, - RelationshipAttribute relationship, - ContextEntity baseEntity, - string[] relationshipChain, - string viewKind = "list", - int depth = 0 - ) - { - var concreteType = typeof(TEntity); - ContextEntity entity; - // we are already getting tags here - var parameters = Expression.Parameter(typeof(TEntity), "model"); - ConstantExpression right; - - IIncludableQueryable temp; - if (relationship.GetType() == typeof(HasManyThroughAttribute)) //assume many-to-many - { - var castRelationship = (HasManyThroughAttribute)relationship; - if (depth > 0) - { - if (depth == 1) - { - // we are at the throughtable so {ArticleTag}, we need {Tag} - // we want to make articleTag => articleTag.Tag - - // {articleTag} - var parameterOne = Expression.Parameter(castRelationship.ThroughType, "articleTag"); - - // {articleTag.Tag} - var body = Expression.PropertyOrField(parameterOne, castRelationship.RightProperty.Name); - - - - // make expression for this one (with logic!) - // REFLECTION - // get logic - var baseType = this.GetType(); - var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeThenIncludeList)); - var genericMethod = method.MakeGenericMethod(new[] { castRelationship.ThroughType, castRelationship.RightProperty.PropertyType }); - var tempEntities = genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); - - - // apply logic - return tempEntities as IIncludableQueryable; - } - - throw new NotImplementedException(); - - } - else - { - - var relationProperty = concreteType.GetProperty(castRelationship.InternalThroughName); - // {article} - var parameterOne = Expression.Parameter(typeof(TEntity), "article"); - if (relationProperty == null) - throw new ArgumentException($"'{castRelationship.InternalRelationshipName}' is not a valid relationship of '{concreteType}'"); - - var relatedType = relationship.Type; - - // {article.ArticleTags} - var left = Expression.PropertyOrField(parameterOne, castRelationship.InternalThroughName); - - // we arent doing anything with it, so just return the right side - var body = left; - // make expression for this one (with logic!) - //var includeExpression = Expression.Lambda>(body, parameterOne); - - - //temp = entities.Include(includeExpression); - // This is the same as - // temp = entities.Include(includeExpression); - var baseType = this.GetType(); - var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeInclude)); - var genericMethod = method.MakeGenericMethod(new[] { castRelationship.ThroughProperty.PropertyType }); - temp = (IIncludableQueryable)genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); - - // Sow heave included {article.articeTags.tags} - //var logic = GetLogic(entity.EntityType) - // we stil need to do some lovely little ThenIncluding - return GetChildren(temp, relationship, baseEntity, relationshipChain, depth: depth + 1); - } - - - } - else - { - var castRelationship = (HasManyAttribute)relationship; - var relationProperty = concreteType.GetProperty(castRelationship.RelationshipPath); - // {article} - var parameterOne = Expression.Parameter(typeof(TEntity), concreteType.ToString()); - if (relationProperty == null) - throw new ArgumentException($"'{castRelationship.InternalRelationshipName}' is not a valid relationship of '{concreteType}'"); - - var relatedType = relationship.Type; - - // {article.ArticleTags} - var left = Expression.PropertyOrField(parameterOne, castRelationship.RelationshipPath); - - // we arent doing anything with it, so just return the right side - var body = left; - if (depth > 0) - { - // make expression for this one (with logic!) - var includeExpression = Expression.Lambda>(body, parameterOne); //yeah.. object,object - - - temp = entities as IIncludableQueryable; - temp.ThenInclude(includeExpression); - } - else - { - var baseType = GetType(); - var method = baseType.GetRuntimeMethods().First(e => e.Name == nameof(MakeIncludeList)); - var genericMethod = method.MakeGenericMethod(new[] { castRelationship.Type }); - temp = (IIncludableQueryable)genericMethod.Invoke(this, new object[] { entities, body, parameterOne }); - } - } - - return entities; - } - - - - public IList ApplyLogic(IList entities, string rel) - { - // seeing as the relationships are already processed, we can just do - // Logic.{method}(articles.Tags) - - IResourceDefinition logic; - var entity = _jsonApiContext.RequestEntity; - - var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == rel); - Type l1 = typeof(List<>); - if (relationship.GetType() == typeof(HasManyThroughAttribute)) - { - var castRelationship = relationship as HasManyThroughAttribute; - object nestedLogic; - for (int index = 0; index < entities.Count(); ++index) - { - // this is our {Article} - var listEntity = entities[index]; - // Get the {Article.ArticleTags} - var relevantProperty = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); - var intermediateEntities = relevantProperty.GetValue(listEntity) as IList; - - // Logic for this nested property - nestedLogic = GetLogic(castRelationship.Type); - - // We default to OnList at the moment - var method = nestedLogic.GetType().GetMethods().First(e => e.Name == "OnList"); - - // get Tags, this can be replaced with some SelectMany's - Type constructed = l1.MakeGenericType(castRelationship.RightProperty.PropertyType); - - // Not happy with this, but was needed. - IList toHold = Activator.CreateInstance(constructed) as IList; - foreach (var iEntity in intermediateEntities) - { - // Iterating over the {ArticleTag}s to get all the {Tag}s - var fetchedTags = (iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance).GetValue(iEntity)); - toHold.Add(fetchedTags); - } - // - - IEnumerable filteredTags = method.Invoke(nestedLogic, new object[] { toHold }) as IEnumerable; - toHold = filteredTags as IList; - - var toHoldHashSet = (HashSet) GetHashSet((IEnumerable)filteredTags); - // We no process all {Article.ArticleTags} in a for loop because we're changing it - for (int i = 0; i < intermediateEntities.Count; i++) - { - var iEntity = intermediateEntities[i]; - var property = iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance); - - var item = property.GetValue(iEntity); - - if (!toHoldHashSet.ToList().Contains(item)) - { - // What we first did: - //property.SetValue(iEntity, null); - // if {tag} is not filled, we shouldnt fill in {ArticleTag} because otherwise - // JsonApiDotNetCore will try to show a null value, resulting in object refrence not set to an instance of an object - // so we delete it here. - // We shouldnt remove, I'm just emulating the WHERE here... - intermediateEntities.RemoveAt(i); - } - else - { - // dont need to do anything - // Maybe, in the future, when patches are done we need to do - // this: - // intermediateEntities[i] = iEntity; - } - } - PropertyInfo prop = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); - if (null != prop && prop.CanWrite) - { - prop.SetValue(listEntity, intermediateEntities); - } - - - entities[index] = listEntity; - - } - - - } - return entities; - - } - public HashSet GetHashSet(IEnumerable source) - { - return new HashSet(source); - } - private static List ConvertList(List value, Type type) - { - return new List(value.Select(item => (T)Convert.ChangeType(item, type))); - } - public IIncludableQueryable MakeThenIncludeList(IIncludableQueryable> entities, MemberExpression body, ParameterExpression parameter) - { - var expression = Expression.Lambda>(body, parameter); - return entities.ThenInclude(expression); - } - /// - /// Works. For now. - /// - /// - /// - /// - /// - /// - /// - /// - public IIncludableQueryable MakeThenInclude(IIncludableQueryable entities, MemberExpression body, ParameterExpression parameter) - { - var expression = Expression.Lambda>(body, parameter); - return entities.ThenInclude(expression); - } - public IIncludableQueryable MakeInclude(IQueryable entities, MemberExpression body, ParameterExpression parameter) - { - var expression = Expression.Lambda>(body, parameter); - return entities.Include(expression); - } - public IIncludableQueryable MakeIncludeList(IQueryable entities, MemberExpression body, ParameterExpression parameter) - { - var expression = Expression.Lambda>>(body, parameter); - return (IIncludableQueryable < TEntity, TProperty >) entities.Include(expression); - } - - private IIncludableQueryable CustomThenInclude(IIncludableQueryable temp, Expression> includeExpression, Type from, Type to) where TType : class, IIdentifiable - { - Type baseType = temp.GetType(); - MethodInfo getMethod = baseType.GetMethod("ThenInclude", BindingFlags.Public); - MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { from, to }); - return (IIncludableQueryable)genericGet.Invoke(temp, new object[] { includeExpression }); - } - - private IQueryable CallLogic(IQueryable entities, IResourceDefinition resourceDefinition) where TType : class, IIdentifiable - { - Type resourceType = resourceDefinition.GetType(); - MethodInfo getMethod = resourceType.GetMethod("OnList", System.Reflection.BindingFlags.Public); - MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { typeof(TType) }); - - - return (IQueryable)genericGet.Invoke(resourceType, new object[] { entities }); - } - - private IResourceDefinition GetLogic(Type model) - { - return _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), model); - } - - - /// public virtual async Task> PageAsync(IQueryable entities, int pageSize, int pageNumber) { if (pageNumber >= 0) { - return await entities.PageForward(pageSize, pageNumber).ToListAsync(); + // the IQueryable returned from ApplyResourceDefinitionLogic is sometimes consumed here. + // In this case, it does not support .ToListAsync(), so we use the method below. + return await this.ToListAsync(entities.PageForward(pageSize, pageNumber)); } // since EntityFramework does not support IQueryable.Reverse(), we need to know the number of queried entities diff --git a/src/JsonApiDotNetCore/Data/IEntityRepository.cs b/src/JsonApiDotNetCore/Data/IEntityRepository.cs index 9a60d813bc..6a040c7cf5 100644 --- a/src/JsonApiDotNetCore/Data/IEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/IEntityRepository.cs @@ -14,7 +14,7 @@ public interface IEntityRepository IEntityWriteRepository where TEntity : class, IIdentifiable { - //IList ApplyLogic(IList entities, string r); + IQueryable ApplyResourceDefinitionLogic(IQueryable entities, string rel); } /// diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index d1c1de352c..1a7acbbb9e 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -155,7 +155,7 @@ public static void AddJsonApiInternals( services.AddScoped(); services.AddScoped(); - // services.AddScoped(); + services.AddScoped(typeof(IResourceLogicExecutor<>), typeof(ResourceLogicExecutor<>)); } private static void AddOperationServices(IServiceCollection services) diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 6876748bb4..b29139fc59 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -108,6 +108,7 @@ public virtual async Task> GetAsync() if (_jsonApiContext.Options.IncludeTotalRecordCount) _jsonApiContext.PageManager.TotalRecords = await _entities.CountAsync(entities); + entities = _entities.ApplyResourceDefinitionLogic(entities, "tags"); // pagination should be done last since it will execute the query var pagedEntities = await ApplyPageQueryAsync(entities); return pagedEntities; diff --git a/src/JsonApiDotNetCore/Services/IResourceLogicExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceLogicExecutor.cs new file mode 100644 index 0000000000..b7254e77fd --- /dev/null +++ b/src/JsonApiDotNetCore/Services/IResourceLogicExecutor.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace JsonApiDotNetCore.Services +{ + public interface IResourceLogicExecutor + { + IList ApplyLogic(IList entities, string rel); + } +} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs new file mode 100644 index 0000000000..879d1f26d7 --- /dev/null +++ b/src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Services +{ + /// + /// A utility class responsible for executing resource logic as defined in + /// the ResourceDefinition> class (eg. OnList) + /// when the POST, GET, PATC, DELETE etc pipelines are executed. + /// + /// + /// + public class ResourceLogicExecutor : IResourceLogicExecutor where TEntity : class, IIdentifiable + { + protected readonly IJsonApiContext _jsonApiContext; + protected readonly IGenericProcessorFactory _genericProcessorFactory; + //protected readonly ResourceDefinition _resourceDefinition; + + public ResourceLogicExecutor(IJsonApiContext jsonApiContext) + { + _genericProcessorFactory = jsonApiContext.GenericProcessorFactory; + _jsonApiContext = jsonApiContext; + } + + /// + /// + virtual public IList ApplyLogic(IList entities, string rel) + { + // seeing as the relationships are already processed, we can just do + // Logic.{method}(articles.Tags) + + var entity = _jsonApiContext.RequestEntity; + + var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == rel); + Type l1 = typeof(List<>); + if (relationship.GetType() == typeof(HasManyThroughAttribute)) + { + var castRelationship = relationship as HasManyThroughAttribute; + object nestedLogic; + for (int index = 0; index < entities.Count(); ++index) + { + // this is our {Article} + var listEntity = entities[index]; + // Get the {Article.ArticleTags} + var relevantProperty = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); + var intermediateEntities = relevantProperty.GetValue(listEntity) as IList; + + // Logic for this nested property + nestedLogic = GetLogic(castRelationship.Type); + + // We default to OnList at the moment + var method = nestedLogic.GetType().GetMethods().First(e => e.Name == "OnList"); + + // get Tags, this can be replaced with some SelectMany's + Type constructed = l1.MakeGenericType(castRelationship.RightProperty.PropertyType); + + // Not happy with this, but was needed. + IList toHold = Activator.CreateInstance(constructed) as IList; + foreach (var iEntity in intermediateEntities) + { + // Iterating over the {ArticleTag}s to get all the {Tag}s + var fetchedTags = (iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance).GetValue(iEntity)); + toHold.Add(fetchedTags); + } + // + + IEnumerable filteredTags = method.Invoke(nestedLogic, new object[] { toHold }) as IEnumerable; + toHold = filteredTags as IList; + + var toHoldHashSet = (HashSet)GetHashSet((IEnumerable)filteredTags); + // We no process all {Article.ArticleTags} in a for loop because we're changing it + for (int i = 0; i < intermediateEntities.Count; i++) + { + var iEntity = intermediateEntities[i]; + var property = iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance); + + var item = property.GetValue(iEntity); + + if (!toHoldHashSet.ToList().Contains(item)) + { + // What we first did: + //property.SetValue(iEntity, null); + // if {tag} is not filled, we shouldnt fill in {ArticleTag} because otherwise + // JsonApiDotNetCore will try to show a null value, resulting in object refrence not set to an instance of an object + // so we delete it here. + // We shouldnt remove, I'm just emulating the WHERE here... + intermediateEntities.RemoveAt(i); + } + else + { + // dont need to do anything + // Maybe, in the future, when patches are done we need to do + // this: + // intermediateEntities[i] = iEntity; + } + } + PropertyInfo prop = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); + if (null != prop && prop.CanWrite) + { + prop.SetValue(listEntity, intermediateEntities); + } + + + entities[index] = listEntity; + + } + + + } + return entities; + + } + private HashSet GetHashSet(IEnumerable source) + { + return new HashSet(source); + } + private static List ConvertList(List value, Type type) + { + return new List(value.Select(item => (T)Convert.ChangeType(item, type))); + } + + + private IQueryable CallLogic(IQueryable entities, IResourceDefinition resourceDefinition) where TType : class, IIdentifiable + { + Type resourceType = resourceDefinition.GetType(); + MethodInfo getMethod = resourceType.GetMethod("OnList", BindingFlags.Public); + MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { typeof(TType) }); + + + return (IQueryable)genericGet.Invoke(resourceType, new object[] { entities }); + } + + private object GetLogic(Type targetEntity) + { + return _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); + } + + + + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs index 1bb665e523..1ca3d018f4 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs @@ -62,7 +62,8 @@ public async Task Tag_Is_Hidden() var article = _articleFaker.Generate(); var tags = _tagFaker.Generate(2); - tags[0].Name = "THISTAGSHOULDNOTBEVISIBLE"; + string toBeExcluded = "THISTAGSHOULDNOTBEVISIBLE"; + tags[0].Name = toBeExcluded; context.Articles.RemoveRange(context.Articles); await context.SaveChangesAsync(); @@ -106,8 +107,7 @@ public async Task Tag_Is_Hidden() Assert.NotNull(articleResponse); Assert.Equal(article.Name, articleResponse.Name); - - AssertHelper.HasEqualFieldValues(articleResponse.Tags[0], tags[1]); + Assert.DoesNotContain(toBeExcluded, body); } From 870cc2559bba4765457005d2030046778bb125bf Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Fri, 5 Apr 2019 11:27:46 +0200 Subject: [PATCH 008/168] feat: draft setup for "resource hooks" --- .../Data/DefaultEntityRepository.cs | 4 + .../Models/ResourceDefinition.cs | 186 +++++++++++++++++- .../Services/EntityResourceService.cs | 56 +++++- .../Services/ResourceLogicExecutor.cs | 13 ++ 4 files changed, 246 insertions(+), 13 deletions(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 2db07a01e2..c9ceba257f 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -159,6 +159,10 @@ public virtual async Task CreateAsync(TEntity entity) return entity; } + /// + /// @TODO make comments + /// + /// Entity. protected virtual void AttachRelationships(TEntity entity = null) { AttachHasManyPointers(entity); diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index e268f8e08d..5b4e496b80 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -1,5 +1,6 @@ using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Query; +using JsonApiDotNetCore.Services; using System; using System.Collections.Generic; using System.Linq; @@ -12,6 +13,27 @@ namespace JsonApiDotNetCore.Models public interface IResourceDefinition { List GetOutputAttrs(object instance); + } + + public interface IResourceDefinition : IResourceDefinition where T : class, IIdentifiable + { + void BeforeGet(); + IEnumerable AfterGet(List entities); + void BeforeGetSingle(string stringId); + T AfterGetSingle(T entity); + T BeforeCreate(T entity); + void AfterCreate(T entity); + T BeforeUpdate(T entity); + void AfterUpdate(T entity); + void BeforeDelete(T entity); + void AfterDelete(T entity); + void BeforeGetRelationship(string stringId, string relationshipName); + T AfterGetRelationship(T entity); + void BeforeUpdateRelationships(T entity, string relationshipName, List relationships); + void AfterUpdateRelationships(T entity, string relationshipName, List relationships); + + // See the comments of the method implementation for details on this. + // IQueryable OnQueryGet(IQueryable entities); } @@ -165,12 +187,168 @@ private List GetOutputAttrs() /// public virtual QueryFilters GetQueryFilters() => null; + ///// + ///// Executed when listing all resources + ///// + ///// + ///// + //public virtual IEnumerable OnList(List entities, int index) => entities; + //public virtual IEnumerable OnList(HashSet entities) => entities; + + + // GET HOOKS /// - /// Executed when listing all resources + /// @TODO: should query params be passed along to allow fo authorization on requested relations? + /// A hook executed before getting entities. Can be used eg. for authorization. /// - /// - /// - public virtual IEnumerable OnList(List entities) => entities; + public virtual void BeforeGet() { } + /// + /// A hook executed after getting entities. Can be used eg. for publishing events. + /// + /// Can also be used to to filter on the result set of a custom include. For example, + /// if Articles -> Blogs -> Tags are retrieved, the AfterGet method as defined in + /// in all related ResourceDefinitions (if present) will be called: + /// * first for all articles; + /// * then for all blogs; + /// * lastly for all tags. + /// This can be used to build an in-memory filtered include, which is not yet suported by EF Core, + /// see this issue. + /// + /// The (adjusted) entities that result from the query + /// The entities that result from the query + public virtual IEnumerable AfterGet(List entities) => entities; + /// + /// @TODO: should query params be passed along to allow fo authorization on requested relations? + /// A hook executed before getting an individual entity. Can be used eg. for authorization. + /// + /// @TODO Instead of it would be better to have + /// a generic {TId}, but this will requre to change ResourceDefinition{T} into + /// ResourceDefinition{T, TId}. This is probaly better, but not doing this now here + /// to keep it simple. + /// + /// String identifier of the entity to be retrieved + public virtual void BeforeGetSingle(string stringId) { } + /// + /// A hook executed after getting an individual. Can be used eg. for publishing events. + /// + /// Can also be used to to filter on the result set of a custom include. For example, + /// if Articls -> Blogs -> Tags are retrieved, the AfterGet() method as defined in + /// in all related ResourceDefinitions (if present) will be called: + /// * first for the retrieved article; + /// * then for all blogs; + /// * lastly for all tags. + /// This can be used to build an in-memory filtered include, which is not yet suported by EF Core, + /// see this issue. + /// + /// The (adjusted) entity that result from the query + /// The entity that result from the query + public virtual T AfterGetSingle(T entity) => entity; + + /// + /// @TODO [THIS IS FOR LATER] + /// As soon as the filtered include issue + /// has been resolved, we start building the relationship inclusion expression tree ourselves, + /// and we can allow for a "during query" hook like this to allow for in-sql filtering. + /// + /// For now, we are bound to use AfterGet() to achieve the same in-memory after the query has been executed. + /// + /// For now we will not expose this method on . + /// + /// Entities. + public virtual IQueryable OnQueryGet(IQueryable entities) => entities; + + + // CREATE HOOKS + /// + /// A hook executed before creating an entity. Can be used eg. for authorization. + /// If the entity also contains to be created relationships, the BeforeUpdateRelationships() + /// methods on the ResourceDefinition (if implemented) of the entities associated to these relationships + /// will also be called + /// @TODO dubble check if BeforeUpdateRelationships should really be called and how + /// + /// The (adjusted) entity to be created + /// The entity to be created + public virtual T BeforeCreate(T entity) => entity; + /// + /// A hook executed after creating an entity. Can be used eg. for publishing events. + /// + /// The entity that was created + public virtual void AfterCreate(T entity) { } + + // UPDATE HOOKS + /// + /// A hook executed before updating an entity. Can be used eg. for authorization. + /// If the entity also contains to be updated relationships, the BeforeUpdateRelationships() + /// methods on the ResourceDefinition (if implemented) of the entities associated to these relationships + /// will also be called. + /// @TODO dubble check if BeforeUpdateRelationships should really be called and how + /// + /// The (adjusted) entity to be updated + /// The entity to be updated + public virtual T BeforeUpdate(T entity) => entity; + /// + /// A hook executed after updating an entity. Can be used eg. for publishing events. + /// + /// The entity that was updated + public virtual void AfterUpdate(T entity) { } + + // DELETE HOOKS + /// + /// A hook executed before deleting an entity. Can be used eg. for authorization. + /// + /// The entity to be updated + public virtual void BeforeDelete(T entity) { } + /// + /// A hook executed after deleting an entity. Can be used eg. for publishing events. + /// + /// The entity that was deleted + public virtual void AfterDelete(T entity) { } + + + // GET RELATIONSHIPS HOOKS + /// + /// A hook executed before getting a relationship of a particular entity. + /// Can be used eg. for authorization. + /// + /// @TODO it would make more sense to include the actual entity here instead of , + /// but this would require an new/additional query in + /// + /// The id of the "parent" entity + /// Name of the relationship + public virtual void BeforeGetRelationship(string stringId, string relationshipName) { } + /// + /// A hook executed after getting a relationship of a particular entity. + /// Can be used eg. for publishing events. + /// + /// Can be used to construct filtered include, similar to AfterGetSingle(). + /// + /// @TODO: we need to think on how to implement this. Maybe we shoud + /// give user access to parsed relationship object instead of the "parent" + /// entity (the one T where T.Id == stringId)? + /// + /// The (adjusted) parent entity that contains the requested relationship + /// The parent entity that contains the requested relationship + public virtual T AfterGetRelationship(T entity) => entity; + + // UPDATE RELATIONSHIPS HOOKS + /// + /// A hook executed before updating a relationship of a particular entity. + /// @TODO we need to check if it makes sense to expose List{object} relationships + /// to the hook. + /// + /// The "parent" entity of which the relationship is to be updated + /// The name of the relationship to be updated + /// The objects which represent the updated relationships (does this make sense to include?) + public virtual void BeforeUpdateRelationships(T entity, string relationshipName, List relationships) { } + /// + /// A hook executed after updating a relationship of a particular entity. + /// @TODO we need to check if it makes sense to expose List{object} relationships + /// to the hook. + /// + /// The "parent" entity of which the relationship is to be updated + /// The name of the relationship to be updated + /// The objects which represent the updated relationships (does this make sense to include?) + public virtual void AfterUpdateRelationships(T entity, string relationshipName, List relationships) { } diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index b29139fc59..45da5347ff 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -16,8 +16,9 @@ public class EntityResourceService : EntityResourceService entityRepository, + IResourceLogicExecutor logicExecutor = null, ILoggerFactory loggerFactory = null) : - base(jsonApiContext, entityRepository, loggerFactory) + base(jsonApiContext, entityRepository, logicExecutor, loggerFactory) { } } @@ -28,8 +29,9 @@ public class EntityResourceService : EntityResourceService entityRepository, + IResourceLogicExecutor logicExecutor = null, ILoggerFactory loggerFactory = null) : - base(jsonApiContext, entityRepository, loggerFactory) + base(jsonApiContext, entityRepository, logicExecutor, loggerFactory) { } } @@ -42,10 +44,12 @@ public class EntityResourceService : private readonly IEntityRepository _entities; private readonly ILogger _logger; private readonly IResourceMapper _mapper; + private readonly IResourceLogicExecutor _logicExecutor; public EntityResourceService( IJsonApiContext jsonApiContext, IEntityRepository entityRepository, + IResourceLogicExecutor logicExecutor, ILoggerFactory loggerFactory = null) { // no mapper provided, TResource & TEntity must be the same type @@ -74,8 +78,14 @@ public EntityResourceService( public virtual async Task CreateAsync(TResource resource) { var entity = MapIn(resource); - + // @TODO implement logic executor + // NOTE: requires tree traversing if relations are updated!! + // the TRelationEntity logic in its associated resource resource defintion. + // needs to be used. + // entity = _logicExecutor.BeforeCreate(entity) entity = await _entities.CreateAsync(entity); + // @TODO implement logic executor + // _logicExecutor.AfterCreate(resource) // this ensures relationships get reloaded from the database if they have // been requested @@ -84,8 +94,9 @@ public virtual async Task CreateAsync(TResource resource) { if (_entities is IEntityFrameworkRepository efRepository) efRepository.DetachRelationshipPointers(entity); - + return await GetWithRelationshipsAsync(entity.Id); + } return MapOut(entity); @@ -93,11 +104,18 @@ public virtual async Task CreateAsync(TResource resource) public virtual async Task DeleteAsync(TId id) { - return await _entities.DeleteAsync(id); + // @TODO implement logic executor + // _logicExecutor.BeforeDelete(resource) + var succeeded = await _entities.DeleteAsync(id); + // @TODO implement logic executor + // _logicExecutor.AfterDelete(resource) + return succeeded; } public virtual async Task> GetAsync() { + // @TODO implement logic executor + // _logicExecutor.BeforeGet() var entities = _entities.Get(); entities = ApplySortAndFilterQuery(entities); @@ -105,10 +123,13 @@ public virtual async Task> GetAsync() if (ShouldIncludeRelationships()) entities = IncludeRelationships(entities, _jsonApiContext.QuerySet.IncludedRelationships); + // @TODO implement logic executor + // _logicExecutor.AfterGet(entities) + entities = _entities.ApplyResourceDefinitionLogic(entities, "tags"); + if (_jsonApiContext.Options.IncludeTotalRecordCount) _jsonApiContext.PageManager.TotalRecords = await _entities.CountAsync(entities); - - entities = _entities.ApplyResourceDefinitionLogic(entities, "tags"); + // pagination should be done last since it will execute the query var pagedEntities = await ApplyPageQueryAsync(entities); return pagedEntities; @@ -116,20 +137,28 @@ public virtual async Task> GetAsync() public virtual async Task GetAsync(TId id) { + // @TODO implement logic executor + // _logicExecutor.BeforeGetSingle(id) if (ShouldIncludeRelationships()) return await GetWithRelationshipsAsync(id); TEntity entity = await _entities.GetAsync(id); + // @TODO implement logic executor + // _logicExecutor.AfterGetSingle(entity) + return MapOut(entity); } - public virtual async Task GetRelationshipsAsync(TId id, string relationshipName) - => await GetRelationshipAsync(id, relationshipName); + public virtual async Task GetRelationshipsAsync(TId id, string relationshipName) => await GetRelationshipAsync(id, relationshipName); public virtual async Task GetRelationshipAsync(TId id, string relationshipName) { + // @TODO implement logic executor + // _logicExecutor.BeforeGetRelationship(id, relationshipName) var entity = await _entities.GetAndIncludeAsync(id, relationshipName); + // @TODO implement logic executor + // entity = _logicExecutor.AfterGetRelationship(entity) // 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 @@ -153,7 +182,12 @@ public virtual async Task UpdateAsync(TId id, TResource resource) { var entity = MapIn(resource); + // @TODO implement logic executor + // entity = _logicExecutor.BeforeUpdate(id, resource) entity = await _entities.UpdateAsync(id, entity); + // @TODO implement logic executor + // _logicExecutor.AfterUpdate(entity) + return MapOut(entity); } @@ -187,7 +221,11 @@ public virtual async Task UpdateRelationshipsAsync(TId id, string relationshipNa var relationshipIds = relationships.Select(r => r?.Id?.ToString()); + // @TODO implement logic executor + // _logicExecutor.BeforeUpdateRelationships(entity, relationshipName, relationships) await _entities.UpdateRelationshipsAsync(entity, relationship, relationshipIds); + // @TODO implement logic executor + // _logicExecutor.AfterUpdateRelationships(entity, relationshipName, relationships) relationship.Type = relationshipType; } diff --git a/src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs index 879d1f26d7..4e3a88a4f7 100644 --- a/src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs @@ -28,7 +28,20 @@ public ResourceLogicExecutor(IJsonApiContext jsonApiContext) } /// + /// kijk op root niveau (TEntity) + /// haal resourcedefinition op van TEntity + /// entities = resourcedefinition.applyresourcelogic(entities) + /// return als geen relatiestring + /// of + /// recursief verdr met dezelfde functie als wel relatie string. + /// + /// + /// OnList(HashSet allOccuringEntities) + /// ONList(List entities /// + /// The logic. + /// Entities. + /// Rel. virtual public IList ApplyLogic(IList entities, string rel) { // seeing as the relationships are already processed, we can just do From f2373dadcdf97406b6059b6fdb6163c69b777f6e Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Fri, 5 Apr 2019 15:07:41 +0200 Subject: [PATCH 009/168] feat: introduced ResourceHookExecutor and ResourceHookContainer abstraction. Added foundation for reflective discovery of hook implementations in IImplementedResourceHooks --- .../Resources/TagResource.cs | 2 +- .../Resources/UserResource.cs | 2 +- .../Data/DefaultEntityRepository.cs | 23 +-- .../IServiceCollectionExtensions.cs | 3 +- .../Internal/ImplementedResourceHooks.cs | 83 +++++++++++ .../Models/ResourceDefinition.cs | 23 +-- .../Services/EntityResourceService.cs | 74 +++++----- .../Services/IResourceHookExecutor.cs | 35 +++++ .../Services/IResourceLogicExecutor.cs | 9 -- ...gicExecutor.cs => ResourceHookExecutor.cs} | 137 +++++++++++++++--- .../Services/EntityResourceService_Tests.cs | 3 +- 11 files changed, 293 insertions(+), 101 deletions(-) create mode 100644 src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs create mode 100644 src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs delete mode 100644 src/JsonApiDotNetCore/Services/IResourceLogicExecutor.cs rename src/JsonApiDotNetCore/Services/{ResourceLogicExecutor.cs => ResourceHookExecutor.cs} (59%) diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs index e732803a77..c18ec3d613 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class TagResource : ResourceDefinition { - public override IEnumerable OnList(List entities) + public override IEnumerable AfterGet(List entities) { return entities.Where(t => t.Name != "THISTAGSHOULDNOTBEVISIBLE").ToList(); } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index a408dc3a4a..46160f98e4 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -10,6 +10,6 @@ public class UserResource : ResourceDefinition protected override List OutputAttrs() => Remove(user => user.Password); - public override IEnumerable OnList(List entities) => entities; + public override IEnumerable AfterGet(List entities) => entities; } } diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index c9ceba257f..359cefd1b7 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -22,18 +22,18 @@ public class DefaultEntityRepository public DefaultEntityRepository( IJsonApiContext jsonApiContext, IDbContextResolver contextResolver, - IResourceLogicExecutor logicExecutor = null + IResourceHookExecutor hookExecutor = null ) - : base(jsonApiContext, contextResolver, logicExecutor) + : base(jsonApiContext, contextResolver, hookExecutor) { } public DefaultEntityRepository( ILoggerFactory loggerFactory, IJsonApiContext jsonApiContext, IDbContextResolver contextResolver, - IResourceLogicExecutor logicExecutor = null + IResourceHookExecutor hookExecutor = null ) - : base(loggerFactory, jsonApiContext, contextResolver, logicExecutor) + : base(loggerFactory, jsonApiContext, contextResolver, hookExecutor) { } } @@ -52,26 +52,26 @@ public class DefaultEntityRepository private readonly IJsonApiContext _jsonApiContext; private readonly IGenericProcessorFactory _genericProcessorFactory; private readonly ResourceDefinition _resourceDefinition; - private readonly IResourceLogicExecutor _logicExecutor; + private readonly IResourceHookExecutor _hookExecutor; public DefaultEntityRepository( IJsonApiContext jsonApiContext, IDbContextResolver contextResolver, - IResourceLogicExecutor logicExecutor = null + IResourceHookExecutor hookExecutor = null ) { _context = contextResolver.GetContext(); _dbSet = contextResolver.GetDbSet(); _jsonApiContext = jsonApiContext; _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; - _logicExecutor = logicExecutor; + _hookExecutor = hookExecutor; } public DefaultEntityRepository( ILoggerFactory loggerFactory, IJsonApiContext jsonApiContext, IDbContextResolver contextResolver, - IResourceLogicExecutor logicExecutor = null, + IResourceHookExecutor hookExecutor = null, ResourceDefinition resourceDefinition = null ) { @@ -81,7 +81,7 @@ public DefaultEntityRepository( _logger = loggerFactory.CreateLogger>(); _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; _resourceDefinition = resourceDefinition; - _logicExecutor = logicExecutor; + _hookExecutor = hookExecutor; } /// @@ -171,8 +171,9 @@ protected virtual void AttachRelationships(TEntity entity = null) public virtual IQueryable ApplyResourceDefinitionLogic(IQueryable entities, string rel) { - if (_logicExecutor == null) return entities; - return _logicExecutor.ApplyLogic(entities.ToList(), rel).AsQueryable(); + if (_hookExecutor == null) return entities; + //return _hookExecutor.ExecuteHook(entities.ToList(), rel).AsQueryable(); + return entities; // In development! For the sake of not getting build errors for now. } /// diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index 1a7acbbb9e..0d84442add 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -155,7 +155,8 @@ public static void AddJsonApiInternals( services.AddScoped(); services.AddScoped(); - services.AddScoped(typeof(IResourceLogicExecutor<>), typeof(ResourceLogicExecutor<>)); + services.AddScoped(typeof(IResourceHookExecutor<>), typeof(ResourceHookExecutor<>)); + services.AddSingleton(typeof(IImplementedResourceHooks<>), typeof(ImplementedResourceHooks<>)); } private static void AddOperationServices(IServiceCollection services) diff --git a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs new file mode 100644 index 0000000000..21e5552b6a --- /dev/null +++ b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs @@ -0,0 +1,83 @@ +using System; +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Internal +{ + public enum ResourceHook + { + BeforeGet, + AfterGet, + BeforeGetSingle, + AfterGetSingle, + BeforeCreate, + AfterCreate, + BeforeUpdate, + AfterUpdate, + BeforeDelete, + AfterDelete, + BeforeGetRelationship, + AfterGetRelationship, + BeforeUpdateRelationships, + AfterUpdateRelationships + } + + /// + /// A singleton service for a particular TEntity that stores a field of + /// enums that represents which hooks have been implemented for a particular + /// entity. + /// + public interface IImplementedResourceHooks where TEntity : class, IIdentifiable + { + ResourceHook[] ImplementedHooks { get; } + } + + /// + /// The default implementation for IImplementedResourceHooks + /// + public class ImplementedResourceHooks : IImplementedResourceHooks where TEntity : class, IIdentifiable + { + private bool _isInitialized; + public ResourceHook[] ImplementedHooks { get; private set; } + + public ImplementedResourceHooks() + { + DiscoverImplementedHooksForModel(); + } + + /// + /// Discovers the implemented hooks for a model. + /// + /// The implemented hooks for model. + void DiscoverImplementedHooksForModel() + { + if (!_isInitialized) + { + _isInitialized = true; + } + else + { + throw new Exception($@" + Implemented hooks may be discovered only once. + Adding such implementations at runtime is not supported. + "); + } + + // Do reflective discovery of implemented hooks: + // eg, for a model Article, it should discover if there is declared a class + // ResourceDefinition
, and if so, will reflectively discover + // which of the methods of IResourceHookContainer
have a + // custom implementation. For these methods, include them in a + // ResourceHook[] and the publically ImplementedHooks. + // Hardcoding this for now. + ImplementedHooks = new ResourceHook[] { ResourceHook.BeforeGet, + ResourceHook.AfterGet, ResourceHook.BeforeGetSingle, + ResourceHook.AfterGetSingle, ResourceHook.BeforeCreate, + ResourceHook.AfterCreate, ResourceHook.BeforeUpdate, + ResourceHook.AfterUpdate, ResourceHook.BeforeDelete, + ResourceHook.AfterDelete, ResourceHook.BeforeGetRelationship, + ResourceHook.AfterGetRelationship, ResourceHook.BeforeUpdateRelationships, + ResourceHook.AfterUpdateRelationships }; + + } + } +} diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 5b4e496b80..8cabae8f18 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -15,27 +15,6 @@ public interface IResourceDefinition List GetOutputAttrs(object instance); } - public interface IResourceDefinition : IResourceDefinition where T : class, IIdentifiable - { - void BeforeGet(); - IEnumerable AfterGet(List entities); - void BeforeGetSingle(string stringId); - T AfterGetSingle(T entity); - T BeforeCreate(T entity); - void AfterCreate(T entity); - T BeforeUpdate(T entity); - void AfterUpdate(T entity); - void BeforeDelete(T entity); - void AfterDelete(T entity); - void BeforeGetRelationship(string stringId, string relationshipName); - T AfterGetRelationship(T entity); - void BeforeUpdateRelationships(T entity, string relationshipName, List relationships); - void AfterUpdateRelationships(T entity, string relationshipName, List relationships); - - // See the comments of the method implementation for details on this. - // IQueryable OnQueryGet(IQueryable entities); - - } /// /// exposes developer friendly hooks into how their resources are exposed. @@ -44,7 +23,7 @@ public interface IResourceDefinition : IResourceDefinition where T : class, /// service and repository layers. /// /// The resource type - public class ResourceDefinition : IResourceDefinition where T : class, IIdentifiable + public class ResourceDefinition : IResourceDefinition, IResourceHookContainer where T : class, IIdentifiable { private readonly IResourceGraph _graph; private readonly ContextEntity _contextEntity; diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 45da5347ff..56edb36774 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -16,9 +16,9 @@ public class EntityResourceService : EntityResourceService entityRepository, - IResourceLogicExecutor logicExecutor = null, + IResourceHookExecutor _hookExecutor = null, ILoggerFactory loggerFactory = null) : - base(jsonApiContext, entityRepository, logicExecutor, loggerFactory) + base(jsonApiContext, entityRepository, _hookExecutor, loggerFactory) { } } @@ -29,9 +29,9 @@ public class EntityResourceService : EntityResourceService entityRepository, - IResourceLogicExecutor logicExecutor = null, + IResourceHookExecutor _hookExecutor = null, ILoggerFactory loggerFactory = null) : - base(jsonApiContext, entityRepository, logicExecutor, loggerFactory) + base(jsonApiContext, entityRepository, _hookExecutor, loggerFactory) { } } @@ -44,12 +44,12 @@ public class EntityResourceService : private readonly IEntityRepository _entities; private readonly ILogger _logger; private readonly IResourceMapper _mapper; - private readonly IResourceLogicExecutor _logicExecutor; + private readonly IResourceHookExecutor _hookExecutor; public EntityResourceService( IJsonApiContext jsonApiContext, IEntityRepository entityRepository, - IResourceLogicExecutor logicExecutor, + IResourceHookExecutor _hookExecutor, ILoggerFactory loggerFactory = null) { // no mapper provided, TResource & TEntity must be the same type @@ -78,14 +78,14 @@ public EntityResourceService( public virtual async Task CreateAsync(TResource resource) { var entity = MapIn(resource); - // @TODO implement logic executor + // @TODO implement hook executor // NOTE: requires tree traversing if relations are updated!! // the TRelationEntity logic in its associated resource resource defintion. // needs to be used. - // entity = _logicExecutor.BeforeCreate(entity) + // entity = _hookExecutor.BeforeCreate(entity) entity = await _entities.CreateAsync(entity); - // @TODO implement logic executor - // _logicExecutor.AfterCreate(resource) + // @TODO implement hook executor + // _hookExecutor.AfterCreate(resource) // this ensures relationships get reloaded from the database if they have // been requested @@ -94,7 +94,7 @@ public virtual async Task CreateAsync(TResource resource) { if (_entities is IEntityFrameworkRepository efRepository) efRepository.DetachRelationshipPointers(entity); - + return await GetWithRelationshipsAsync(entity.Id); } @@ -104,18 +104,18 @@ public virtual async Task CreateAsync(TResource resource) public virtual async Task DeleteAsync(TId id) { - // @TODO implement logic executor - // _logicExecutor.BeforeDelete(resource) + // @TODO implement hook executor + // _hookExecutor.BeforeDelete(resource) var succeeded = await _entities.DeleteAsync(id); - // @TODO implement logic executor - // _logicExecutor.AfterDelete(resource) + // @TODO implement hook executor + // _hookExecutor.AfterDelete(resource) return succeeded; } public virtual async Task> GetAsync() { - // @TODO implement logic executor - // _logicExecutor.BeforeGet() + // @TODO implement hook executor + // _hookExecutor.BeforeGet() var entities = _entities.Get(); entities = ApplySortAndFilterQuery(entities); @@ -123,13 +123,13 @@ public virtual async Task> GetAsync() if (ShouldIncludeRelationships()) entities = IncludeRelationships(entities, _jsonApiContext.QuerySet.IncludedRelationships); - // @TODO implement logic executor - // _logicExecutor.AfterGet(entities) - entities = _entities.ApplyResourceDefinitionLogic(entities, "tags"); + // @TODO implement hook executor + // _hookExecutor.AfterGet(entities) + //entities = _entities.ApplyResourceDefinitionLogic(entities, "tags"); if (_jsonApiContext.Options.IncludeTotalRecordCount) _jsonApiContext.PageManager.TotalRecords = await _entities.CountAsync(entities); - + // pagination should be done last since it will execute the query var pagedEntities = await ApplyPageQueryAsync(entities); return pagedEntities; @@ -137,15 +137,15 @@ public virtual async Task> GetAsync() public virtual async Task GetAsync(TId id) { - // @TODO implement logic executor - // _logicExecutor.BeforeGetSingle(id) + // @TODO implement hook executor + // _hookExecutor.BeforeGetSingle(id) if (ShouldIncludeRelationships()) return await GetWithRelationshipsAsync(id); TEntity entity = await _entities.GetAsync(id); - // @TODO implement logic executor - // _logicExecutor.AfterGetSingle(entity) + // @TODO implement hook executor + // _hookExecutor.AfterGetSingle(entity) return MapOut(entity); } @@ -154,11 +154,11 @@ public virtual async Task GetAsync(TId id) public virtual async Task GetRelationshipAsync(TId id, string relationshipName) { - // @TODO implement logic executor - // _logicExecutor.BeforeGetRelationship(id, relationshipName) + // @TODO implement hook executor + // _hookExecutor.BeforeGetRelationship(id, relationshipName) var entity = await _entities.GetAndIncludeAsync(id, relationshipName); - // @TODO implement logic executor - // entity = _logicExecutor.AfterGetRelationship(entity) + // @TODO implement hook executor + // entity = _hookExecutor.AfterGetRelationship(entity) // 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 @@ -182,11 +182,11 @@ public virtual async Task UpdateAsync(TId id, TResource resource) { var entity = MapIn(resource); - // @TODO implement logic executor - // entity = _logicExecutor.BeforeUpdate(id, resource) + // @TODO implement hook executor + // entity = _hookExecutor.BeforeUpdate(id, resource) entity = await _entities.UpdateAsync(id, entity); - // @TODO implement logic executor - // _logicExecutor.AfterUpdate(entity) + // @TODO implement hook executor + // _hookExecutor.AfterUpdate(entity) return MapOut(entity); @@ -221,11 +221,11 @@ public virtual async Task UpdateRelationshipsAsync(TId id, string relationshipNa var relationshipIds = relationships.Select(r => r?.Id?.ToString()); - // @TODO implement logic executor - // _logicExecutor.BeforeUpdateRelationships(entity, relationshipName, relationships) + // @TODO implement hook executor + // _hookExecutor.BeforeUpdateRelationships(entity, relationshipName, relationships) await _entities.UpdateRelationshipsAsync(entity, relationship, relationshipIds); - // @TODO implement logic executor - // _logicExecutor.AfterUpdateRelationships(entity, relationshipName, relationships) + // @TODO implement hook executor + // _hookExecutor.AfterUpdateRelationships(entity, relationshipName, relationships) relationship.Type = relationshipType; } diff --git a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs new file mode 100644 index 0000000000..260b5c07f5 --- /dev/null +++ b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Services +{ + + + public interface IResourceHookContainer where T : class, IIdentifiable + { + void BeforeGet(); + IEnumerable AfterGet(List entities); + void BeforeGetSingle(string stringId); + T AfterGetSingle(T entity); + T BeforeCreate(T entity); + void AfterCreate(T entity); + T BeforeUpdate(T entity); + void AfterUpdate(T entity); + void BeforeDelete(T entity); + void AfterDelete(T entity); + void BeforeGetRelationship(string stringId, string relationshipName); + T AfterGetRelationship(T entity); + void BeforeUpdateRelationships(T entity, string relationshipName, List relationships); + void AfterUpdateRelationships(T entity, string relationshipName, List relationships); + + // See the comments of the method implementation for details on this. + // IQueryable OnQueryGet(IQueryable entities); + } + + public interface IResourceHookExecutor : IResourceHookContainer where T : class, IIdentifiable + { + bool ShouldExecuteHook(ResourceHook hook); + + } +} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Services/IResourceLogicExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceLogicExecutor.cs deleted file mode 100644 index b7254e77fd..0000000000 --- a/src/JsonApiDotNetCore/Services/IResourceLogicExecutor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace JsonApiDotNetCore.Services -{ - public interface IResourceLogicExecutor - { - IList ApplyLogic(IList entities, string rel); - } -} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs similarity index 59% rename from src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs rename to src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index 4e3a88a4f7..ab2400eff3 100644 --- a/src/JsonApiDotNetCore/Services/ResourceLogicExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; @@ -12,37 +13,136 @@ namespace JsonApiDotNetCore.Services /// A utility class responsible for executing resource logic as defined in /// the ResourceDefinition> class (eg. OnList) /// when the POST, GET, PATC, DELETE etc pipelines are executed. - /// - /// /// - public class ResourceLogicExecutor : IResourceLogicExecutor where TEntity : class, IIdentifiable + public class ResourceHookExecutor : IResourceHookExecutor where TEntity : class, IIdentifiable { + + protected readonly ResourceHook[] _implementedHooks; protected readonly IJsonApiContext _jsonApiContext; protected readonly IGenericProcessorFactory _genericProcessorFactory; //protected readonly ResourceDefinition _resourceDefinition; - public ResourceLogicExecutor(IJsonApiContext jsonApiContext) + public ResourceHookExecutor(IJsonApiContext jsonApiContext, IImplementedResourceHooks hooksConfiguration) { _genericProcessorFactory = jsonApiContext.GenericProcessorFactory; _jsonApiContext = jsonApiContext; + _implementedHooks = hooksConfiguration.ImplementedHooks; } + virtual public bool ShouldExecuteHook(ResourceHook hook) + { + return _implementedHooks.Contains(hook); + } + + + /// + /// @TODO Implementation overview to be described here + /// + public virtual void BeforeGet() + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual IEnumerable AfterGet(List entities) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual void BeforeGetSingle(string stringId) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual TEntity AfterGetSingle(TEntity entity) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual IQueryable OnQueryGet(IQueryable entities) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual TEntity BeforeCreate(TEntity entity) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual void AfterCreate(TEntity entity) + { + throw new NotImplementedException(); + } /// - /// kijk op root niveau (TEntity) - /// haal resourcedefinition op van TEntity - /// entities = resourcedefinition.applyresourcelogic(entities) - /// return als geen relatiestring - /// of - /// recursief verdr met dezelfde functie als wel relatie string. - /// - /// - /// OnList(HashSet allOccuringEntities) - /// ONList(List entities + /// @TODO Implementation overview to be described here /// - /// The logic. - /// Entities. - /// Rel. - virtual public IList ApplyLogic(IList entities, string rel) + public virtual TEntity BeforeUpdate(TEntity entity) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual void AfterUpdate(TEntity entity) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual void BeforeDelete(TEntity entity) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual void AfterDelete(TEntity entity) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual void BeforeGetRelationship(string stringId, string relationshipName) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual TEntity AfterGetRelationship(TEntity entity) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual void BeforeUpdateRelationships(TEntity entity, string relationshipName, List relationships) + { + throw new NotImplementedException(); + } + /// + /// @TODO Implementation overview to be described here + /// + public virtual void AfterUpdateRelationships(TEntity entity, string relationshipName, List relationships) + { + throw new NotImplementedException(); + } + + + virtual public IList ExecuteHook(IList entities, string rel) { // seeing as the relationships are already processed, we can just do // Logic.{method}(articles.Tags) @@ -148,6 +248,7 @@ private IQueryable CallLogic(IQueryable entities, IResource return (IQueryable)genericGet.Invoke(resourceType, new object[] { entities }); } + private object GetLogic(Type targetEntity) { return _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); diff --git a/test/UnitTests/Services/EntityResourceService_Tests.cs b/test/UnitTests/Services/EntityResourceService_Tests.cs index 4380a6622b..44cb6bce0b 100644 --- a/test/UnitTests/Services/EntityResourceService_Tests.cs +++ b/test/UnitTests/Services/EntityResourceService_Tests.cs @@ -15,6 +15,7 @@ public class EntityResourceService_Tests private readonly Mock _jsonApiContextMock = new Mock(); private readonly Mock> _repositoryMock = new Mock>(); private readonly ILoggerFactory _loggerFactory = new Mock().Object; + private readonly Mock> _hookExecutor = new Mock>(); public EntityResourceService_Tests() { @@ -73,6 +74,6 @@ public async Task GetRelationshipAsync_Returns_Relationship_Value() } private EntityResourceService GetService() => - new EntityResourceService(_jsonApiContextMock.Object, _repositoryMock.Object, _loggerFactory); + new EntityResourceService(_jsonApiContextMock.Object, _repositoryMock.Object, _hookExecutor.Object,_loggerFactory); } } From cf294f8d87ea391526209bbd5cdf965be6133cf6 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 8 Apr 2019 16:52:13 +0200 Subject: [PATCH 010/168] feat: improved proposal resource hooks --- .../Resources/TagResource.cs | 3 +- .../Resources/UserResource.cs | 6 +- .../Internal/Generics/GenericProcessor.cs | 9 + .../Internal/ImplementedResourceHooks.cs | 46 +-- .../Models/ResourceDefinition.cs | 197 ++-------- .../Services/EntityResourceService.cs | 73 ++-- .../Services/IResourceHookExecutor.cs | 126 ++++++- .../Services/ResourceHookExecutor.cs | 345 ++++++++---------- 8 files changed, 387 insertions(+), 418 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs index c18ec3d613..b756ee7f81 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; using JsonApiDotNetCoreExample.Models; @@ -7,7 +8,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class TagResource : ResourceDefinition { - public override IEnumerable AfterGet(List entities) + public override IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) { return entities.Where(t => t.Name != "THISTAGSHOULDNOTBEVISIBLE").ToList(); } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index 46160f98e4..694a3865d0 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; using JsonApiDotNetCoreExample.Models; @@ -10,6 +11,9 @@ public class UserResource : ResourceDefinition protected override List OutputAttrs() => Remove(user => user.Password); - public override IEnumerable AfterGet(List entities) => entities; + public override IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) + { + return entities; + } } } diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs index 8a98745465..a55c7be3b9 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs @@ -94,13 +94,22 @@ private async Task SetRelationshipsAsync(object parent, RelationshipAttribute re { // TODO: need to handle the failure mode when the relationship does not implement IIdentifiable var entities = _context.Set().Where(x => relationshipIds.Contains(((IIdentifiable)x).StringId)).ToList(); + + // @TODO implement hook executor + // entities.Select( e => _hookExecutor.BeforeUpdate(e, ResourceAction.UpdateRelationships)) relationship.SetValue(parent, entities); + // @TODO implement hook executor + // entities.ForEach( e => _hookExecutor.AfterUpdate(e, ResourceAction.UpdateRelationships)) } else { // TODO: need to handle the failure mode when the relationship does not implement IIdentifiable var entity = _context.Set().SingleOrDefault(x => relationshipIds.First() == ((IIdentifiable)x).StringId); + // @TODO implement hook executor + // entity = _hookExecutor.BeforeUpdate(entity, ResourceAction.UpdateRelationships) relationship.SetValue(parent, entity); + // @TODO implement hook executor + // _hookExecutor.AfterUpdate(entity, ResourceAction.UpdateRelationships) } await _context.SaveChangesAsync(); diff --git a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs index 21e5552b6a..e1b603df32 100644 --- a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs +++ b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs @@ -3,22 +3,27 @@ namespace JsonApiDotNetCore.Internal { + public enum ResourceAction + { + Get, + GetSingle, + GetRelationship, + Create, + Patch, + PatchRelationships, + Delete + } + public enum ResourceHook { - BeforeGet, - AfterGet, - BeforeGetSingle, - AfterGetSingle, BeforeCreate, AfterCreate, + BeforeRead, + AfterRead, BeforeUpdate, AfterUpdate, BeforeDelete, - AfterDelete, - BeforeGetRelationship, - AfterGetRelationship, - BeforeUpdateRelationships, - AfterUpdateRelationships + AfterDelete } /// @@ -56,10 +61,8 @@ void DiscoverImplementedHooksForModel() } else { - throw new Exception($@" - Implemented hooks may be discovered only once. - Adding such implementations at runtime is not supported. - "); + throw new JsonApiSetupException($@" Implemented hooks may be discovered only once. + Adding such implementations at runtime is currently not supported."); } // Do reflective discovery of implemented hooks: @@ -69,14 +72,15 @@ Adding such implementations at runtime is not supported. // custom implementation. For these methods, include them in a // ResourceHook[] and the publically ImplementedHooks. // Hardcoding this for now. - ImplementedHooks = new ResourceHook[] { ResourceHook.BeforeGet, - ResourceHook.AfterGet, ResourceHook.BeforeGetSingle, - ResourceHook.AfterGetSingle, ResourceHook.BeforeCreate, - ResourceHook.AfterCreate, ResourceHook.BeforeUpdate, - ResourceHook.AfterUpdate, ResourceHook.BeforeDelete, - ResourceHook.AfterDelete, ResourceHook.BeforeGetRelationship, - ResourceHook.AfterGetRelationship, ResourceHook.BeforeUpdateRelationships, - ResourceHook.AfterUpdateRelationships }; + ImplementedHooks = new ResourceHook[] { + ResourceHook.BeforeCreate, + ResourceHook.AfterCreate, + ResourceHook.BeforeRead, + ResourceHook.BeforeUpdate, + ResourceHook.AfterUpdate, + ResourceHook.BeforeDelete, + ResourceHook.AfterDelete + }; } } diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 8cabae8f18..12aba635c5 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -166,170 +166,53 @@ private List GetOutputAttrs() /// public virtual QueryFilters GetQueryFilters() => null; - ///// - ///// Executed when listing all resources - ///// - ///// - ///// - //public virtual IEnumerable OnList(List entities, int index) => entities; - //public virtual IEnumerable OnList(HashSet entities) => entities; - - - // GET HOOKS - /// - /// @TODO: should query params be passed along to allow fo authorization on requested relations? - /// A hook executed before getting entities. Can be used eg. for authorization. - /// - public virtual void BeforeGet() { } - /// - /// A hook executed after getting entities. Can be used eg. for publishing events. - /// - /// Can also be used to to filter on the result set of a custom include. For example, - /// if Articles -> Blogs -> Tags are retrieved, the AfterGet method as defined in - /// in all related ResourceDefinitions (if present) will be called: - /// * first for all articles; - /// * then for all blogs; - /// * lastly for all tags. - /// This can be used to build an in-memory filtered include, which is not yet suported by EF Core, - /// see this issue. - /// - /// The (adjusted) entities that result from the query - /// The entities that result from the query - public virtual IEnumerable AfterGet(List entities) => entities; - /// - /// @TODO: should query params be passed along to allow fo authorization on requested relations? - /// A hook executed before getting an individual entity. Can be used eg. for authorization. - /// - /// @TODO Instead of it would be better to have - /// a generic {TId}, but this will requre to change ResourceDefinition{T} into - /// ResourceDefinition{T, TId}. This is probaly better, but not doing this now here - /// to keep it simple. - /// - /// String identifier of the entity to be retrieved - public virtual void BeforeGetSingle(string stringId) { } - /// - /// A hook executed after getting an individual. Can be used eg. for publishing events. - /// - /// Can also be used to to filter on the result set of a custom include. For example, - /// if Articls -> Blogs -> Tags are retrieved, the AfterGet() method as defined in - /// in all related ResourceDefinitions (if present) will be called: - /// * first for the retrieved article; - /// * then for all blogs; - /// * lastly for all tags. - /// This can be used to build an in-memory filtered include, which is not yet suported by EF Core, - /// see this issue. - /// - /// The (adjusted) entity that result from the query - /// The entity that result from the query - public virtual T AfterGetSingle(T entity) => entity; - - /// - /// @TODO [THIS IS FOR LATER] - /// As soon as the filtered include issue - /// has been resolved, we start building the relationship inclusion expression tree ourselves, - /// and we can allow for a "during query" hook like this to allow for in-sql filtering. - /// - /// For now, we are bound to use AfterGet() to achieve the same in-memory after the query has been executed. - /// - /// For now we will not expose this method on . - /// - /// Entities. - public virtual IQueryable OnQueryGet(IQueryable entities) => entities; - - - // CREATE HOOKS - /// - /// A hook executed before creating an entity. Can be used eg. for authorization. - /// If the entity also contains to be created relationships, the BeforeUpdateRelationships() - /// methods on the ResourceDefinition (if implemented) of the entities associated to these relationships - /// will also be called - /// @TODO dubble check if BeforeUpdateRelationships should really be called and how - /// - /// The (adjusted) entity to be created - /// The entity to be created - public virtual T BeforeCreate(T entity) => entity; - /// - /// A hook executed after creating an entity. Can be used eg. for publishing events. - /// - /// The entity that was created - public virtual void AfterCreate(T entity) { } + /// + public virtual T BeforeCreate(T entity, ResourceAction actionSource) + { + return entity; + } - // UPDATE HOOKS - /// - /// A hook executed before updating an entity. Can be used eg. for authorization. - /// If the entity also contains to be updated relationships, the BeforeUpdateRelationships() - /// methods on the ResourceDefinition (if implemented) of the entities associated to these relationships - /// will also be called. - /// @TODO dubble check if BeforeUpdateRelationships should really be called and how - /// - /// The (adjusted) entity to be updated - /// The entity to be updated - public virtual T BeforeUpdate(T entity) => entity; - /// - /// A hook executed after updating an entity. Can be used eg. for publishing events. - /// - /// The entity that was updated - public virtual void AfterUpdate(T entity) { } + /// + public virtual T AfterCreate(T entity, ResourceAction actionSource) + { + return entity; + } - // DELETE HOOKS - /// - /// A hook executed before deleting an entity. Can be used eg. for authorization. - /// - /// The entity to be updated - public virtual void BeforeDelete(T entity) { } - /// - /// A hook executed after deleting an entity. Can be used eg. for publishing events. - /// - /// The entity that was deleted - public virtual void AfterDelete(T entity) { } + /// + public virtual void BeforeRead(ResourceAction actionSource, string stringId = null) + { + return; + } + /// + public virtual IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) + { + return entities; + } - // GET RELATIONSHIPS HOOKS - /// - /// A hook executed before getting a relationship of a particular entity. - /// Can be used eg. for authorization. - /// - /// @TODO it would make more sense to include the actual entity here instead of , - /// but this would require an new/additional query in - /// - /// The id of the "parent" entity - /// Name of the relationship - public virtual void BeforeGetRelationship(string stringId, string relationshipName) { } - /// - /// A hook executed after getting a relationship of a particular entity. - /// Can be used eg. for publishing events. - /// - /// Can be used to construct filtered include, similar to AfterGetSingle(). - /// - /// @TODO: we need to think on how to implement this. Maybe we shoud - /// give user access to parsed relationship object instead of the "parent" - /// entity (the one T where T.Id == stringId)? - /// - /// The (adjusted) parent entity that contains the requested relationship - /// The parent entity that contains the requested relationship - public virtual T AfterGetRelationship(T entity) => entity; + /// + public virtual T BeforeUpdate(T entity, ResourceAction actionSource) + { + return entity; + } - // UPDATE RELATIONSHIPS HOOKS - /// - /// A hook executed before updating a relationship of a particular entity. - /// @TODO we need to check if it makes sense to expose List{object} relationships - /// to the hook. - /// - /// The "parent" entity of which the relationship is to be updated - /// The name of the relationship to be updated - /// The objects which represent the updated relationships (does this make sense to include?) - public virtual void BeforeUpdateRelationships(T entity, string relationshipName, List relationships) { } - /// - /// A hook executed after updating a relationship of a particular entity. - /// @TODO we need to check if it makes sense to expose List{object} relationships - /// to the hook. - /// - /// The "parent" entity of which the relationship is to be updated - /// The name of the relationship to be updated - /// The objects which represent the updated relationships (does this make sense to include?) - public virtual void AfterUpdateRelationships(T entity, string relationshipName, List relationships) { } + /// + public virtual T AfterUpdate(T entity, ResourceAction actionSource) + { + return entity; + } + /// + public virtual void BeforeDelete(T entity, ResourceAction actionSource) + { + return; + } + /// + public virtual void AfterDelete(T entity, bool succeeded, ResourceAction actionSource) + { + return; + } /// /// This is an alias type intended to simplify the implementation's diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 56edb36774..e1b2124b0c 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -79,13 +79,9 @@ public virtual async Task CreateAsync(TResource resource) { var entity = MapIn(resource); // @TODO implement hook executor - // NOTE: requires tree traversing if relations are updated!! - // the TRelationEntity logic in its associated resource resource defintion. - // needs to be used. - // entity = _hookExecutor.BeforeCreate(entity) + // entity = _hookExecutor.BeforeCreate(entity, ResourceAction.Create) entity = await _entities.CreateAsync(entity); - // @TODO implement hook executor - // _hookExecutor.AfterCreate(resource) + // this ensures relationships get reloaded from the database if they have // been requested @@ -95,27 +91,35 @@ public virtual async Task CreateAsync(TResource resource) if (_entities is IEntityFrameworkRepository efRepository) efRepository.DetachRelationshipPointers(entity); - return await GetWithRelationshipsAsync(entity.Id); + entity = await GetWithRelationshipsAsync(entity.Id); } + // @TODO implement hook executor + // entity = _hookExecutor.AfterCreate(entity, ResourceAction.Create) return MapOut(entity); } public virtual async Task DeleteAsync(TId id) { + // @TODO the implementation of the DeleteAsync on repo level needs + // to be changed for it to work intuitively with hooks. + // Specifically, we need: + // var entity = await _entities.Get(id) + // var succeeded = await _entities.DeleteAsync(entity); + // so that we can use the entity variable in our hook execution. // @TODO implement hook executor - // _hookExecutor.BeforeDelete(resource) + // _hookExecutor.BeforeDelete(entity, ResourceAction.Delete) var succeeded = await _entities.DeleteAsync(id); // @TODO implement hook executor - // _hookExecutor.AfterDelete(resource) + // _hookExecutor.AfterDelete(entity, succeeded, ResourceAction.Delete) return succeeded; } public virtual async Task> GetAsync() { // @TODO implement hook executor - // _hookExecutor.BeforeGet() + // _hookExecutor.BeforeRead(ResourceAction.Read) var entities = _entities.Get(); entities = ApplySortAndFilterQuery(entities); @@ -124,8 +128,9 @@ public virtual async Task> GetAsync() entities = IncludeRelationships(entities, _jsonApiContext.QuerySet.IncludedRelationships); // @TODO implement hook executor - // _hookExecutor.AfterGet(entities) - //entities = _entities.ApplyResourceDefinitionLogic(entities, "tags"); + // _hookExecutor.AfterRead(entities, ResourceAction.Read) + // note: The hookexecutor will also fire the BeforeRead and AfterRead hooks + // for every included entity. if (_jsonApiContext.Options.IncludeTotalRecordCount) _jsonApiContext.PageManager.TotalRecords = await _entities.CountAsync(entities); @@ -138,27 +143,36 @@ public virtual async Task> GetAsync() public virtual async Task GetAsync(TId id) { // @TODO implement hook executor - // _hookExecutor.BeforeGetSingle(id) + // _hookExecutor.BeforeRead(ResourceAction.ReadSingle, id) + //TResource entity = null; + TEntity entity; if (ShouldIncludeRelationships()) - return await GetWithRelationshipsAsync(id); - - TEntity entity = await _entities.GetAsync(id); - + { + entity = await GetWithRelationshipsAsync(id); + } else + { + entity = await _entities.GetAsync(id); + } // @TODO implement hook executor - // _hookExecutor.AfterGetSingle(entity) - + // entity = _hookExecutor.AfterRead(entity, ResourceAction.ReadSingle).SingleOrDefault(); + // note: The hookexecutor will also fire the BeforeRead and AfterRead hooks + // for every included entity. return MapOut(entity); } + // triggered by route /articles/1/relationships/{relationshipName} public virtual async Task GetRelationshipsAsync(TId id, string relationshipName) => await GetRelationshipAsync(id, relationshipName); + // triggered by route /articles/1/{relationshipName} public virtual async Task GetRelationshipAsync(TId id, string relationshipName) { // @TODO implement hook executor - // _hookExecutor.BeforeGetRelationship(id, relationshipName) + // _hookExecutor.BeforeRead(id, ResourceAction.GetRelationship) var entity = await _entities.GetAndIncludeAsync(id, relationshipName); // @TODO implement hook executor - // entity = _hookExecutor.AfterGetRelationship(entity) + // entity = _hookExecutor.AfterRead(entity, ResourceAction.GetRelationship).SingleOrDefault(); + // note: The hookexecutor will also fire the BeforeRead and AfterRead hooks + // for every included entity. // 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 @@ -183,10 +197,10 @@ public virtual async Task UpdateAsync(TId id, TResource resource) var entity = MapIn(resource); // @TODO implement hook executor - // entity = _hookExecutor.BeforeUpdate(id, resource) + // entity = _hookExecutor.BeforeUpdate(entity, ResourceAction.Update) entity = await _entities.UpdateAsync(id, entity); // @TODO implement hook executor - // _hookExecutor.AfterUpdate(entity) + // entity = _hookExecutor.AfterUpdate(entity, ResourceAction.Update) return MapOut(entity); @@ -221,11 +235,16 @@ public virtual async Task UpdateRelationshipsAsync(TId id, string relationshipNa var relationshipIds = relationships.Select(r => r?.Id?.ToString()); + // @TODO implement hook executor - // _hookExecutor.BeforeUpdateRelationships(entity, relationshipName, relationships) + // entity = _hookExecutor.BeforeUpdate(entity, ResourceAction.UpdateRelationships) await _entities.UpdateRelationshipsAsync(entity, relationship, relationshipIds); + // Note the call in previous line relies on Generic Processor to update relations. + // In this call, _hookExecutor will call the BeforeUpdate and AfterUpdate + // hooks for the target relation entities. See the SetRelationshipsAsync + // method in GenericProcessor.cs // @TODO implement hook executor - // _hookExecutor.AfterUpdateRelationships(entity, relationshipName, relationships) + // _hookExecutor.AfterUpdate(entity, ResourceAction.UpdateRelationships) relationship.Type = relationshipType; } @@ -277,7 +296,7 @@ protected virtual IQueryable IncludeRelationships(IQueryable e return entities; } - private async Task GetWithRelationshipsAsync(TId id) + private async Task GetWithRelationshipsAsync(TId id) { var query = _entities.Get().Where(e => e.Id.Equals(id)); @@ -293,7 +312,7 @@ private async Task GetWithRelationshipsAsync(TId id) else value = await _entities.FirstOrDefaultAsync(query); - return MapOut(value); + return value; } private bool ShouldIncludeRelationships() diff --git a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs index 260b5c07f5..969a01c589 100644 --- a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs @@ -8,27 +8,119 @@ namespace JsonApiDotNetCore.Services public interface IResourceHookContainer where T : class, IIdentifiable { - void BeforeGet(); - IEnumerable AfterGet(List entities); - void BeforeGetSingle(string stringId); - T AfterGetSingle(T entity); - T BeforeCreate(T entity); - void AfterCreate(T entity); - T BeforeUpdate(T entity); - void AfterUpdate(T entity); - void BeforeDelete(T entity); - void AfterDelete(T entity); - void BeforeGetRelationship(string stringId, string relationshipName); - T AfterGetRelationship(T entity); - void BeforeUpdateRelationships(T entity, string relationshipName, List relationships); - void AfterUpdateRelationships(T entity, string relationshipName, List relationships); - - // See the comments of the method implementation for details on this. - // IQueryable OnQueryGet(IQueryable entities); + /// + /// A hook executed before creating an entity. Can be used eg. for authorization. + /// If the entity also contains to be created relationships, the BeforeUpdate + /// and AfterUpdate hooks for those relations will be called too. + /// + /// This hook is executed from: + /// * EntityResourceService.CreateAsync + /// + /// The (adjusted) entity to be created + /// The entity to be created + /// The pipeline from which the hook was called + T BeforeCreate(T entity, ResourceAction actionSource); + + /// + /// A hook executed after creating an entity. Can be used eg. for publishing events. + /// + /// This hook is executed from: + /// * EntityResourceService.CreateAsync + /// + /// The entity that was created + /// The pipeline from which the hook was called + T AfterCreate(T entity, ResourceAction actionSource); + + + /// + /// A hook executed after before reading entities. Can be used eg. for logging, authorization. + /// + /// This hook is executed from: + /// * EntityResourceService.GetAsync() + /// * EntityResourceService.GetAsync(TId id) + /// * GetRelationshipsAsync.GetAsync(TId id) + /// + /// + /// The entities that result from the query + /// If the + /// The pipeline from which the hook was called + void BeforeRead(ResourceAction actionSource, string stringId = null); + + + /// + /// A hook executed after reading entities. Can be used eg. for publishing events. + /// + /// Can also be used to to filter on the result set of a custom include. For example, + /// if Articles -> Blogs -> Tags are retrieved, the AfterRead method as defined in + /// in all related ResourceDefinitions (if present) will be called: + /// * first for all articles; + /// * then for all blogs; + /// * lastly for all tags. + /// This can be used to build an in-memory filtered include, which is not yet suported by EF Core, + /// see this issue. + /// + /// This hook is executed from: + /// * EntityResourceService.GetAsync() + /// * EntityResourceService.GetAsync(TId id) + /// * GetRelationshipsAsync.GetAsync(TId id) + /// + /// The (adjusted) entities that result from the query + /// The entities that result from the query + /// The pipeline from which the hook was called + IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource); + + + /// + /// A hook executed before updating an entity. Can be used eg. for authorization. + /// If the entity also contains to be updated relationships, the BeforeUpdate() + /// and AfterUdpate() hooks for the entities of those relationships are also executed. + /// + /// This hook is executed from: + /// * EntityResourceService.UpdateAsync + /// + /// The (adjusted) entity to be updated + /// The entity to be updated + /// The pipeline from which the hook was called + T BeforeUpdate(T entity, ResourceAction actionSource); + + + /// + /// A hook executed after updating an entity. Can be used eg. for publishing an event. + /// + /// This hook is executed from: + /// * EntityResourceService.UpdateAsync + /// + /// The entity that was updated + /// The pipeline from which the hook was called + T AfterUpdate(T entity, ResourceAction actionSource); + + + /// + /// A hook executed before deleting an entity. Can be used eg. for authorization. + /// + /// The entity to be deleted + /// The pipeline from which the hook was called + void BeforeDelete(T entity, ResourceAction actionSource); + + /// + /// A hook executed before deleting an entity. Can be used eg. for publishing an event. + /// + /// The entity to be deleted + /// The pipeline from which the hook was called + /// A boolean to indicate whether the deletion was succesful + void AfterDelete(T entity, bool succeeded, ResourceAction actionSource); + + } public interface IResourceHookExecutor : IResourceHookContainer where T : class, IIdentifiable { + /// + /// Checks whether a hook should be executed or not, by reflectively + /// verifying if a hook is implemented on IResourceModel> + /// + /// true, if execute hook should be executed, false otherwise. + /// The enum representing the type of hook. bool ShouldExecuteHook(ResourceHook hook); } diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index ab2400eff3..622542c202 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCore.Services { /// - /// A utility class responsible for executing resource logic as defined in + /// A utility class responsible for executing hooks as defined in /// the ResourceDefinition> class (eg. OnList) /// when the POST, GET, PATC, DELETE etc pipelines are executed. /// @@ -20,7 +20,7 @@ public class ResourceHookExecutor : IResourceHookExecutor wher protected readonly ResourceHook[] _implementedHooks; protected readonly IJsonApiContext _jsonApiContext; protected readonly IGenericProcessorFactory _genericProcessorFactory; - //protected readonly ResourceDefinition _resourceDefinition; + protected readonly ResourceDefinition _resourceDefinition; public ResourceHookExecutor(IJsonApiContext jsonApiContext, IImplementedResourceHooks hooksConfiguration) { @@ -29,230 +29,187 @@ public ResourceHookExecutor(IJsonApiContext jsonApiContext, IImplementedResource _implementedHooks = hooksConfiguration.ImplementedHooks; } - virtual public bool ShouldExecuteHook(ResourceHook hook) + public virtual bool ShouldExecuteHook(ResourceHook hook) { return _implementedHooks.Contains(hook); } - - /// - /// @TODO Implementation overview to be described here - /// - public virtual void BeforeGet() - { - throw new NotImplementedException(); - } - /// - /// @TODO Implementation overview to be described here - /// - public virtual IEnumerable AfterGet(List entities) - { - throw new NotImplementedException(); - } - /// - /// @TODO Implementation overview to be described here - /// - public virtual void BeforeGetSingle(string stringId) - { - throw new NotImplementedException(); - } - /// - /// @TODO Implementation overview to be described here - /// - public virtual TEntity AfterGetSingle(TEntity entity) - { - throw new NotImplementedException(); - } - /// - /// @TODO Implementation overview to be described here - /// - public virtual IQueryable OnQueryGet(IQueryable entities) - { - throw new NotImplementedException(); - } - /// - /// @TODO Implementation overview to be described here - /// - public virtual TEntity BeforeCreate(TEntity entity) - { - throw new NotImplementedException(); - } - /// - /// @TODO Implementation overview to be described here - /// - public virtual void AfterCreate(TEntity entity) - { - throw new NotImplementedException(); - } - /// - /// @TODO Implementation overview to be described here - /// - public virtual TEntity BeforeUpdate(TEntity entity) - { - throw new NotImplementedException(); - } - /// - /// @TODO Implementation overview to be described here - /// - public virtual void AfterUpdate(TEntity entity) - { - throw new NotImplementedException(); - } - /// - /// @TODO Implementation overview to be described here - /// - public virtual void BeforeDelete(TEntity entity) + /// + public virtual TEntity BeforeCreate(TEntity entity, ResourceAction actionSource) { + if (!ShouldExecuteHook(ResourceHook.BeforeCreate)) return entity; + throw new NotImplementedException(); } - /// - /// @TODO Implementation overview to be described here - /// - public virtual void AfterDelete(TEntity entity) + + /// + public virtual TEntity AfterCreate(TEntity entity, ResourceAction actionSource) { + if (!ShouldExecuteHook(ResourceHook.AfterCreate)) return entity; + throw new NotImplementedException(); } - /// - /// @TODO Implementation overview to be described here - /// - public virtual void BeforeGetRelationship(string stringId, string relationshipName) + + /// + public virtual void BeforeRead(ResourceAction actionSource, string stringId = null) { + if (!ShouldExecuteHook(ResourceHook.BeforeRead)) return; + throw new NotImplementedException(); } - /// - /// @TODO Implementation overview to be described here - /// - public virtual TEntity AfterGetRelationship(TEntity entity) + + /// + public virtual IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) { + if (!ShouldExecuteHook(ResourceHook.AfterRead)) return entities; + throw new NotImplementedException(); } - /// - /// @TODO Implementation overview to be described here - /// - public virtual void BeforeUpdateRelationships(TEntity entity, string relationshipName, List relationships) + + /// + public virtual TEntity BeforeUpdate(TEntity entity, ResourceAction actionSource) { + if (!ShouldExecuteHook(ResourceHook.BeforeUpdate)) return entity; + throw new NotImplementedException(); } - /// - /// @TODO Implementation overview to be described here - /// - public virtual void AfterUpdateRelationships(TEntity entity, string relationshipName, List relationships) + + /// + public virtual TEntity AfterUpdate(TEntity entity, ResourceAction actionSource) { + if (!ShouldExecuteHook(ResourceHook.AfterUpdate)) return entity; + throw new NotImplementedException(); } - - virtual public IList ExecuteHook(IList entities, string rel) + /// + public virtual void BeforeDelete(TEntity entity, ResourceAction actionSource) { - // seeing as the relationships are already processed, we can just do - // Logic.{method}(articles.Tags) - - var entity = _jsonApiContext.RequestEntity; - - var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == rel); - Type l1 = typeof(List<>); - if (relationship.GetType() == typeof(HasManyThroughAttribute)) - { - var castRelationship = relationship as HasManyThroughAttribute; - object nestedLogic; - for (int index = 0; index < entities.Count(); ++index) - { - // this is our {Article} - var listEntity = entities[index]; - // Get the {Article.ArticleTags} - var relevantProperty = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); - var intermediateEntities = relevantProperty.GetValue(listEntity) as IList; - - // Logic for this nested property - nestedLogic = GetLogic(castRelationship.Type); - - // We default to OnList at the moment - var method = nestedLogic.GetType().GetMethods().First(e => e.Name == "OnList"); - - // get Tags, this can be replaced with some SelectMany's - Type constructed = l1.MakeGenericType(castRelationship.RightProperty.PropertyType); - - // Not happy with this, but was needed. - IList toHold = Activator.CreateInstance(constructed) as IList; - foreach (var iEntity in intermediateEntities) - { - // Iterating over the {ArticleTag}s to get all the {Tag}s - var fetchedTags = (iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance).GetValue(iEntity)); - toHold.Add(fetchedTags); - } - // - - IEnumerable filteredTags = method.Invoke(nestedLogic, new object[] { toHold }) as IEnumerable; - toHold = filteredTags as IList; - - var toHoldHashSet = (HashSet)GetHashSet((IEnumerable)filteredTags); - // We no process all {Article.ArticleTags} in a for loop because we're changing it - for (int i = 0; i < intermediateEntities.Count; i++) - { - var iEntity = intermediateEntities[i]; - var property = iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance); - - var item = property.GetValue(iEntity); - - if (!toHoldHashSet.ToList().Contains(item)) - { - // What we first did: - //property.SetValue(iEntity, null); - // if {tag} is not filled, we shouldnt fill in {ArticleTag} because otherwise - // JsonApiDotNetCore will try to show a null value, resulting in object refrence not set to an instance of an object - // so we delete it here. - // We shouldnt remove, I'm just emulating the WHERE here... - intermediateEntities.RemoveAt(i); - } - else - { - // dont need to do anything - // Maybe, in the future, when patches are done we need to do - // this: - // intermediateEntities[i] = iEntity; - } - } - PropertyInfo prop = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); - if (null != prop && prop.CanWrite) - { - prop.SetValue(listEntity, intermediateEntities); - } - - - entities[index] = listEntity; - - } - - - } - return entities; + if (!ShouldExecuteHook(ResourceHook.BeforeDelete)) return; + throw new NotImplementedException(); } - private HashSet GetHashSet(IEnumerable source) - { - return new HashSet(source); - } - private static List ConvertList(List value, Type type) - { - return new List(value.Select(item => (T)Convert.ChangeType(item, type))); - } - - private IQueryable CallLogic(IQueryable entities, IResourceDefinition resourceDefinition) where TType : class, IIdentifiable + /// + public virtual void AfterDelete(TEntity entity, bool succeeded, ResourceAction actionSource) { - Type resourceType = resourceDefinition.GetType(); - MethodInfo getMethod = resourceType.GetMethod("OnList", BindingFlags.Public); - MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { typeof(TType) }); - + if (!ShouldExecuteHook(ResourceHook.AfterDelete)) return; - return (IQueryable)genericGet.Invoke(resourceType, new object[] { entities }); + throw new NotImplementedException(); } - private object GetLogic(Type targetEntity) - { - return _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); - } + //virtual public IList ExecuteHook(IList entities, string rel) + //{ + // // seeing as the relationships are already processed, we can just do + // // Logic.{method}(articles.Tags) + + // var entity = _jsonApiContext.RequestEntity; + + // var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == rel); + // Type l1 = typeof(List<>); + // if (relationship.GetType() == typeof(HasManyThroughAttribute)) + // { + // var castRelationship = relationship as HasManyThroughAttribute; + // object nestedLogic; + // for (int index = 0; index < entities.Count(); ++index) + // { + // // this is our {Article} + // var listEntity = entities[index]; + // // Get the {Article.ArticleTags} + // var relevantProperty = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); + // var intermediateEntities = relevantProperty.GetValue(listEntity) as IList; + + // // Logic for this nested property + // nestedLogic = GetLogic(castRelationship.Type); + + // // We default to OnList at the moment + // var method = nestedLogic.GetType().GetMethods().First(e => e.Name == "OnList"); + + // // get Tags, this can be replaced with some SelectMany's + // Type constructed = l1.MakeGenericType(castRelationship.RightProperty.PropertyType); + + // // Not happy with this, but was needed. + // IList toHold = Activator.CreateInstance(constructed) as IList; + // foreach (var iEntity in intermediateEntities) + // { + // // Iterating over the {ArticleTag}s to get all the {Tag}s + // var fetchedTags = (iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance).GetValue(iEntity)); + // toHold.Add(fetchedTags); + // } + // // + + // IEnumerable filteredTags = method.Invoke(nestedLogic, new object[] { toHold }) as IEnumerable; + // toHold = filteredTags as IList; + + // var toHoldHashSet = (HashSet)GetHashSet((IEnumerable)filteredTags); + // // We no process all {Article.ArticleTags} in a for loop because we're changing it + // for (int i = 0; i < intermediateEntities.Count; i++) + // { + // var iEntity = intermediateEntities[i]; + // var property = iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance); + + // var item = property.GetValue(iEntity); + + // if (!toHoldHashSet.ToList().Contains(item)) + // { + // // What we first did: + // //property.SetValue(iEntity, null); + // // if {tag} is not filled, we shouldnt fill in {ArticleTag} because otherwise + // // JsonApiDotNetCore will try to show a null value, resulting in object refrence not set to an instance of an object + // // so we delete it here. + // // We shouldnt remove, I'm just emulating the WHERE here... + // intermediateEntities.RemoveAt(i); + // } + // else + // { + // // dont need to do anything + // // Maybe, in the future, when patches are done we need to do + // // this: + // // intermediateEntities[i] = iEntity; + // } + // } + // PropertyInfo prop = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); + // if (null != prop && prop.CanWrite) + // { + // prop.SetValue(listEntity, intermediateEntities); + // } + + + // entities[index] = listEntity; + + // } + + + // } + // return entities; + + //} + //private HashSet GetHashSet(IEnumerable source) + //{ + // return new HashSet(source); + //} + //private static List ConvertList(List value, Type type) + //{ + // return new List(value.Select(item => (T)Convert.ChangeType(item, type))); + //} + + + //private IQueryable CallLogic(IQueryable entities, IResourceDefinition resourceDefinition) where TType : class, IIdentifiable + //{ + // Type resourceType = resourceDefinition.GetType(); + // MethodInfo getMethod = resourceType.GetMethod("OnList", BindingFlags.Public); + // MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { typeof(TType) }); + + + // return (IQueryable)genericGet.Invoke(resourceType, new object[] { entities }); + //} + + + //private object GetLogic(Type targetEntity) + //{ + // return _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); + //} From 7f8c55014ec14edfd87badd973a0371de7866db4 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 9 Apr 2019 11:25:58 +0200 Subject: [PATCH 011/168] tests: hook discovery test (with implementation) and scaffolded some unit tests --- .../Resources/TagResource.cs | 2 +- .../Resources/UserResource.cs | 2 +- src/JsonApiDotNetCore/Graph/TypeLocator.cs | 2 +- .../Internal/ImplementedResourceHooks.cs | 41 ++++++---------- .../Services/EntityResourceService.cs | 13 ++--- .../Services/IResourceHookExecutor.cs | 16 ++++++- .../Models/ResourceDefinitionTests.cs | 8 ++-- test/UnitTests/Models/ResourceHookTests.cs | 48 +++++++++++++++++++ 8 files changed, 91 insertions(+), 41 deletions(-) create mode 100644 test/UnitTests/Models/ResourceHookTests.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs index b756ee7f81..4755ae4941 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; namespace JsonApiDotNetCoreExample.Resources diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index 694a3865d0..5bf31f9c75 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; namespace JsonApiDotNetCoreExample.Resources diff --git a/src/JsonApiDotNetCore/Graph/TypeLocator.cs b/src/JsonApiDotNetCore/Graph/TypeLocator.cs index f96e17ffe0..610b813428 100644 --- a/src/JsonApiDotNetCore/Graph/TypeLocator.cs +++ b/src/JsonApiDotNetCore/Graph/TypeLocator.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCore.Graph /// /// Used to locate types and facilitate auto-resource discovery /// - internal static class TypeLocator + static class TypeLocator { private static Dictionary _typeCache = new Dictionary(); private static Dictionary> _identifiableTypeCache = new Dictionary>(); diff --git a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs index e1b603df32..354d829685 100644 --- a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs +++ b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs @@ -1,19 +1,12 @@ using System; +using System.Linq; +using System.Reflection; +using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; namespace JsonApiDotNetCore.Internal { - public enum ResourceAction - { - Get, - GetSingle, - GetRelationship, - Create, - Patch, - PatchRelationships, - Delete - } - public enum ResourceHook { BeforeCreate, @@ -41,6 +34,7 @@ public interface IImplementedResourceHooks where TEntity : class, IIden /// public class ImplementedResourceHooks : IImplementedResourceHooks where TEntity : class, IIdentifiable { + private readonly ResourceHook[] _allHooks = Enum.GetValues(typeof(ResourceHook)) as ResourceHook[]; private bool _isInitialized; public ResourceHook[] ImplementedHooks { get; private set; } @@ -65,23 +59,16 @@ void DiscoverImplementedHooksForModel() Adding such implementations at runtime is currently not supported."); } - // Do reflective discovery of implemented hooks: - // eg, for a model Article, it should discover if there is declared a class - // ResourceDefinition
, and if so, will reflectively discover - // which of the methods of IResourceHookContainer
have a - // custom implementation. For these methods, include them in a - // ResourceHook[] and the publically ImplementedHooks. - // Hardcoding this for now. - ImplementedHooks = new ResourceHook[] { - ResourceHook.BeforeCreate, - ResourceHook.AfterCreate, - ResourceHook.BeforeRead, - ResourceHook.BeforeUpdate, - ResourceHook.AfterUpdate, - ResourceHook.BeforeDelete, - ResourceHook.AfterDelete - }; + Type resourceDefinitionImplementationType = null; + + foreach (var match in TypeLocator.GetDerivedTypes(typeof(TEntity).Assembly, typeof(ResourceDefinition))) + { + resourceDefinitionImplementationType = match; + break; + } + ImplementedHooks = _allHooks.Where(h => resourceDefinitionImplementationType.GetMethod(h.ToString("G")).DeclaringType == resourceDefinitionImplementationType) + .ToArray(); } } } diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index e1b2124b0c..cd3c18d21c 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -16,9 +16,9 @@ public class EntityResourceService : EntityResourceService entityRepository, - IResourceHookExecutor _hookExecutor = null, + IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) : - base(jsonApiContext, entityRepository, _hookExecutor, loggerFactory) + base(jsonApiContext, entityRepository, hookExecutor, loggerFactory) { } } @@ -29,9 +29,9 @@ public class EntityResourceService : EntityResourceService entityRepository, - IResourceHookExecutor _hookExecutor = null, + IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) : - base(jsonApiContext, entityRepository, _hookExecutor, loggerFactory) + base(jsonApiContext, entityRepository, hookExecutor, loggerFactory) { } } @@ -44,12 +44,12 @@ public class EntityResourceService : private readonly IEntityRepository _entities; private readonly ILogger _logger; private readonly IResourceMapper _mapper; - private readonly IResourceHookExecutor _hookExecutor; + private readonly IResourceHookExecutor _hookExecutor; public EntityResourceService( IJsonApiContext jsonApiContext, IEntityRepository entityRepository, - IResourceHookExecutor _hookExecutor, + IResourceHookExecutor hookExecutor, ILoggerFactory loggerFactory = null) { // no mapper provided, TResource & TEntity must be the same type @@ -60,6 +60,7 @@ public EntityResourceService( _jsonApiContext = jsonApiContext; _entities = entityRepository; + _hookExecutor = hookExecutor; _logger = loggerFactory?.CreateLogger>(); } diff --git a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs index 969a01c589..deaf69ac6d 100644 --- a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs @@ -4,7 +4,21 @@ namespace JsonApiDotNetCore.Services { - + /// + /// An enum that represents the initiator of a hook. Eg, when BeforeCreate() + /// is called from EntityResourceService.GetAsync(TId id), it will be called + /// with parameter actionSource = ResourceAction.GetSingle. + /// + public enum ResourceAction + { + Get, + GetSingle, + GetRelationship, + Create, + Patch, + PatchRelationships, + Delete + } public interface IResourceHookContainer where T : class, IIdentifiable { diff --git a/test/UnitTests/Models/ResourceDefinitionTests.cs b/test/UnitTests/Models/ResourceDefinitionTests.cs index f058dbc946..bf2a3ff291 100644 --- a/test/UnitTests/Models/ResourceDefinitionTests.cs +++ b/test/UnitTests/Models/ResourceDefinitionTests.cs @@ -84,7 +84,7 @@ public void InstanceOutputAttrsAreSpecified_Returns_True_If_Instance_Method_Is_O // assert Assert.True(resource._instanceAttrsAreSpecified); } - + [Fact] public void InstanceOutputAttrsAreSpecified_Returns_False_If_Instance_Method_Is_Not_Overriden() { @@ -119,7 +119,7 @@ protected override List OutputAttrs() => _isAdmin ? Remove(m => m.AlwaysExcluded) : Remove(m => new { m.AlwaysExcluded, m.Password }, from: base.OutputAttrs()); - + public override QueryFilters GetQueryFilters() => new QueryFilters { { "is-active", (query, value) => query.Select(x => x) } @@ -130,7 +130,7 @@ protected override PropertySortOrder GetDefaultSortOrder() (t => t.Prop, SortDirection.Ascending) }; } - + public class InstanceFilteredResource : ResourceDefinition { // Called once per resource instance @@ -139,4 +139,4 @@ protected override List OutputAttrs(Model model) ? Remove(m => m.AlwaysExcluded, base.OutputAttrs()) : Remove(m => new { m.AlwaysExcluded, m.Password }, from: base.OutputAttrs()); } -} +} \ No newline at end of file diff --git a/test/UnitTests/Models/ResourceHookTests.cs b/test/UnitTests/Models/ResourceHookTests.cs new file mode 100644 index 0000000000..32712b36dc --- /dev/null +++ b/test/UnitTests/Models/ResourceHookTests.cs @@ -0,0 +1,48 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Query; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using System.Collections.Generic; +using Xunit; + +namespace UnitTests.Models +{ + public class Dummy : Identifiable + { + + } + + public class DummyResourceDefinition : ResourceDefinition + { + public override void BeforeDelete(Dummy entity, ResourceAction actionSource) + { + } + public override void AfterDelete(Dummy entity, bool succeeded, ResourceAction actionSource) + { + + } + } + + public class ResourceHooks_Tests + { + + public ResourceHooks_Tests() + { + + } + + [Fact] + public void Hook_Discovery() + { + var hookConfig = new ImplementedResourceHooks(); + Assert.Contains(ResourceHook.BeforeDelete, hookConfig.ImplementedHooks); + Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); + Assert.Equal(2, hookConfig.ImplementedHooks.Length); + + + } + } +} From 826ee9a61cad43df3468fa1b45f227d25285bc1a Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 9 Apr 2019 12:53:11 +0200 Subject: [PATCH 012/168] feat: wired up AfterDelete and BeforeDelete, created tests for all of them --- .../Services/IResourceHookExecutor.cs | 12 +- .../Services/ResourceHookExecutor.cs | 19 +- test/UnitTests/Models/ResourceHookTests.cs | 167 ++++++++++++++++-- 3 files changed, 168 insertions(+), 30 deletions(-) diff --git a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs index deaf69ac6d..e10437664c 100644 --- a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs @@ -45,7 +45,6 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// The pipeline from which the hook was called T AfterCreate(T entity, ResourceAction actionSource); - /// /// A hook executed after before reading entities. Can be used eg. for logging, authorization. /// @@ -60,7 +59,6 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// The pipeline from which the hook was called void BeforeRead(ResourceAction actionSource, string stringId = null); - /// /// A hook executed after reading entities. Can be used eg. for publishing events. /// @@ -83,7 +81,6 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// The pipeline from which the hook was called IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource); - /// /// A hook executed before updating an entity. Can be used eg. for authorization. /// If the entity also contains to be updated relationships, the BeforeUpdate() @@ -97,7 +94,6 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// The pipeline from which the hook was called T BeforeUpdate(T entity, ResourceAction actionSource); - /// /// A hook executed after updating an entity. Can be used eg. for publishing an event. /// @@ -108,7 +104,6 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// The pipeline from which the hook was called T AfterUpdate(T entity, ResourceAction actionSource); - /// /// A hook executed before deleting an entity. Can be used eg. for authorization. /// @@ -123,19 +118,16 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// The pipeline from which the hook was called /// A boolean to indicate whether the deletion was succesful void AfterDelete(T entity, bool succeeded, ResourceAction actionSource); - - } public interface IResourceHookExecutor : IResourceHookContainer where T : class, IIdentifiable { /// - /// Checks whether a hook should be executed or not, by reflectively - /// verifying if a hook is implemented on IResourceModel> + /// Checks whether a hook should be executed or not through reflective + /// verification if a hook is implemented on IResourceModel> /// /// true, if execute hook should be executed, false otherwise. /// The enum representing the type of hook. bool ShouldExecuteHook(ResourceHook hook); - } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index 622542c202..3a9151c807 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -29,6 +29,7 @@ public ResourceHookExecutor(IJsonApiContext jsonApiContext, IImplementedResource _implementedHooks = hooksConfiguration.ImplementedHooks; } + /// public virtual bool ShouldExecuteHook(ResourceHook hook) { return _implementedHooks.Contains(hook); @@ -86,19 +87,24 @@ public virtual TEntity AfterUpdate(TEntity entity, ResourceAction actionSource) public virtual void BeforeDelete(TEntity entity, ResourceAction actionSource) { if (!ShouldExecuteHook(ResourceHook.BeforeDelete)) return; - - throw new NotImplementedException(); + var hookContainer = GetResourceDefinition(typeof(TEntity)); + hookContainer.BeforeDelete(entity, actionSource); } /// public virtual void AfterDelete(TEntity entity, bool succeeded, ResourceAction actionSource) { if (!ShouldExecuteHook(ResourceHook.AfterDelete)) return; - - throw new NotImplementedException(); + var hookContainer = GetResourceDefinition(typeof(TEntity)); + hookContainer.AfterDelete(entity, succeeded, actionSource); } + private IResourceHookContainer GetResourceDefinition(Type targetEntity) + { + return (IResourceHookContainer)_genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); + } + //virtual public IList ExecuteHook(IList entities, string rel) //{ // // seeing as the relationships are already processed, we can just do @@ -206,10 +212,7 @@ public virtual void AfterDelete(TEntity entity, bool succeeded, ResourceAction a //} - //private object GetLogic(Type targetEntity) - //{ - // return _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); - //} + diff --git a/test/UnitTests/Models/ResourceHookTests.cs b/test/UnitTests/Models/ResourceHookTests.cs index 32712b36dc..43df21ea4d 100644 --- a/test/UnitTests/Models/ResourceHookTests.cs +++ b/test/UnitTests/Models/ResourceHookTests.cs @@ -1,15 +1,138 @@ using JsonApiDotNetCore.Builders; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Query; +using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample.Models; +using Moq; +using System; using System.Collections.Generic; using Xunit; namespace UnitTests.Models { + public class ResourceHooks_Tests + { + + + public ResourceHooks_Tests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder().Build(); + + } + + [Fact] + public void Hook_Discovery() + { + var hookConfig = new ImplementedResourceHooks(); + Assert.Contains(ResourceHook.BeforeDelete, hookConfig.ImplementedHooks); + Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); + Assert.Equal(2, hookConfig.ImplementedHooks.Length); + } + + [Fact] + public void BeforeCreate_Hook_Is_Called() + { + (var resourceDefinitionMock, var contextMock) = CreateMocks(); + + var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); + hookExecutor.BeforeCreate(It.IsAny(), It.IsAny()); + resourceDefinitionMock.Verify(rd => rd.BeforeCreate(It.IsAny(), It.IsAny()), Times.Once()); + } + + [Fact] + public void AfterCreate_Hook_Is_Called() + { + (var resourceDefinitionMock, var contextMock) = CreateMocks(); + + var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); + hookExecutor.AfterCreate(It.IsAny(), It.IsAny()); + resourceDefinitionMock.Verify(rd => rd.AfterCreate(It.IsAny(), It.IsAny()), Times.Once()); + } + [Fact] + public void BeforeRead_Hook_Is_Called() + { + (var resourceDefinitionMock, var contextMock) = CreateMocks(); + + var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); + hookExecutor.BeforeRead(It.IsAny()); + resourceDefinitionMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + } + [Fact] + public void AfterRead_Hook_Is_Called() + { + (var resourceDefinitionMock, var contextMock) = CreateMocks(); + + var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); + hookExecutor.AfterRead(It.IsAny>(), It.IsAny()); + resourceDefinitionMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + } + [Fact] + public void BeforeUpdate_Hook_Is_Called() + { + (var resourceDefinitionMock, var contextMock) = CreateMocks(); + + var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); + hookExecutor.BeforeUpdate(It.IsAny(), It.IsAny()); + resourceDefinitionMock.Verify(rd => rd.BeforeUpdate(It.IsAny(), It.IsAny()), Times.Once()); + } + [Fact] + public void AfterUpdate_Hook_Is_Called() + { + (var resourceDefinitionMock, var contextMock) = CreateMocks(); + + var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); + hookExecutor.AfterUpdate(It.IsAny(), It.IsAny()); + resourceDefinitionMock.Verify(rd => rd.AfterUpdate(It.IsAny(), It.IsAny()), Times.Once()); + } + [Fact] + public void BeforeDelete_Hook_Is_Called() + { + (var resourceDefinitionMock, var contextMock) = CreateMocks(); + + var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); + hookExecutor.BeforeDelete(It.IsAny(), It.IsAny()); + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny(), It.IsAny()), Times.Once()); + } + [Fact] + public void AfterDelete_Hook_Is_Called() + { + (var resourceDefinitionMock, var contextMock) = CreateMocks(); + + var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); + hookExecutor.AfterDelete(It.IsAny(), true, It.IsAny()); + resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny(), true, It.IsAny()), Times.Once()); + } + Mock CreateResourceDefinitionMock() + { + var resourceDefinition = new Mock(); + resourceDefinition.Setup(rd => rd.BeforeCreate(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterCreate(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.BeforeRead(It.IsAny(), null)); + resourceDefinition.Setup(rd => rd.AfterRead(It.IsAny>(), It.IsAny())); + resourceDefinition.Setup(rd => rd.BeforeUpdate(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterUpdate(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.BeforeDelete(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterDelete(It.IsAny(), It.IsAny(), It.IsAny())); + return resourceDefinition; + } + + (Mock, Mock) CreateMocks() + { + + var resourceDefinition = CreateResourceDefinitionMock(); + + var processorFactory = new Mock(); + processorFactory.Setup(c => c.GetProcessor(It.IsAny(), It.IsAny())) + .Returns((IResourceDefinition)resourceDefinition.Object); + var context = new Mock(); + context.Setup(c => c.GenericProcessorFactory).Returns(processorFactory.Object); + + return (resourceDefinition, context); + } + } public class Dummy : Identifiable { @@ -26,22 +149,42 @@ public override void AfterDelete(Dummy entity, bool succeeded, ResourceAction ac } } - public class ResourceHooks_Tests + public class DummyWithAllHooks : Identifiable { - public ResourceHooks_Tests() - { + } + public class DummyWithAllHooksResourceDefinition : ResourceDefinition + { + public override DummyWithAllHooks BeforeCreate(DummyWithAllHooks entity, ResourceAction actionSource) + { + return entity; } - - [Fact] - public void Hook_Discovery() + public override DummyWithAllHooks AfterCreate(DummyWithAllHooks entity, ResourceAction actionSource) + { + return entity; + } + public override void BeforeRead(ResourceAction actionSource, string stringId = null) { - var hookConfig = new ImplementedResourceHooks(); - Assert.Contains(ResourceHook.BeforeDelete, hookConfig.ImplementedHooks); - Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); - Assert.Equal(2, hookConfig.ImplementedHooks.Length); + } + public override IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) + { + return entities; + } + public override DummyWithAllHooks BeforeUpdate(DummyWithAllHooks entity, ResourceAction actionSource) + { + return entity; + } + public override DummyWithAllHooks AfterUpdate(DummyWithAllHooks entity, ResourceAction actionSource) + { + return entity; + } + public override void BeforeDelete(DummyWithAllHooks entity, ResourceAction actionSource) + { + } + public override void AfterDelete(DummyWithAllHooks entity, bool succeeded, ResourceAction actionSource) + { } } From 3aa512387d19fd14fa46f992fdcfc2ac1cf97f32 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 9 Apr 2019 13:05:34 +0200 Subject: [PATCH 013/168] chore: explanations added to classes involved in hook execution --- .../Services/IResourceHookExecutor.cs | 26 +++++++++++++++++++ .../Services/ResourceHookExecutor.cs | 6 +---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs index e10437664c..5a73c95079 100644 --- a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs @@ -20,6 +20,14 @@ public enum ResourceAction Delete } + + /// + /// An interface that should be implemented by the class that defines resource hooks. + /// + /// By default, the ResourceDefinition> class inherits + /// from IResourceHookContainer>, which means that the + /// hooks can be implemented on ResourceDefinition>. + /// public interface IResourceHookContainer where T : class, IIdentifiable { /// @@ -120,6 +128,24 @@ public interface IResourceHookContainer where T : class, IIdentifiable void AfterDelete(T entity, bool succeeded, ResourceAction actionSource); } + + /// + /// A utility class responsible for executing hooks as defined in + /// the IResourceHookContainer>. + /// + /// The hook execution flow is as follows: + /// 1. The EntityResourceService> instance (service instance) + /// holds a reference (through dependency injection) to the executor instance. + /// 2. When the eg. DeleteAsync() method on the service instance is called, the service instance + /// calls the BeforeDelete and AfterDelete methods on the hook executor instance. + /// 3. The hook executor instance is then responsible for getting access to and calling the hooks + /// that are defined on IResourceHookContainer> (which + /// by default is the ResourceDefinition implementation). + /// 4. Note that for the simple case of service{Model}.DeleteAsync(), only + /// ResourceDefinition{Model} is involved. But for more complex operations, like that + /// of service{Model}.GetAsync() with a nested include of related entities, ResourceDefintions for all + /// involved models are resolved and used in a more complex travesal of the resultset. + /// public interface IResourceHookExecutor : IResourceHookContainer where T : class, IIdentifiable { /// diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index 3a9151c807..e8a0b7d0ea 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -9,11 +9,7 @@ namespace JsonApiDotNetCore.Services { - /// - /// A utility class responsible for executing hooks as defined in - /// the ResourceDefinition> class (eg. OnList) - /// when the POST, GET, PATC, DELETE etc pipelines are executed. - /// + /// public class ResourceHookExecutor : IResourceHookExecutor where TEntity : class, IIdentifiable { From 113610e8cce09afab35390fcfb6fea2dd41ebc5a Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 9 Apr 2019 13:07:09 +0200 Subject: [PATCH 014/168] chore: typo in comments --- .../Services/IResourceHookExecutor.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs index 5a73c95079..0b82bbc4de 100644 --- a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs @@ -24,9 +24,12 @@ public enum ResourceAction /// /// An interface that should be implemented by the class that defines resource hooks. /// - /// By default, the ResourceDefinition> class inherits - /// from IResourceHookContainer>, which means that the - /// hooks can be implemented on ResourceDefinition>. + /// By default, the ResourceDefinition class inherits + /// from IResourceHookContainer, which means that the + /// hooks can be implemented on ResourceDefinition. + /// + /// See IResourceHookExecutor for details on the + /// resource hook execution flow. /// public interface IResourceHookContainer where T : class, IIdentifiable { @@ -131,15 +134,15 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// /// A utility class responsible for executing hooks as defined in - /// the IResourceHookContainer>. + /// the IResourceHookContainer. /// /// The hook execution flow is as follows: - /// 1. The EntityResourceService> instance (service instance) + /// 1. The EntityResourceService instance (service instance) /// holds a reference (through dependency injection) to the executor instance. /// 2. When the eg. DeleteAsync() method on the service instance is called, the service instance /// calls the BeforeDelete and AfterDelete methods on the hook executor instance. /// 3. The hook executor instance is then responsible for getting access to and calling the hooks - /// that are defined on IResourceHookContainer> (which + /// that are defined on IResourceHookContainer (which /// by default is the ResourceDefinition implementation). /// 4. Note that for the simple case of service{Model}.DeleteAsync(), only /// ResourceDefinition{Model} is involved. But for more complex operations, like that @@ -150,7 +153,7 @@ public interface IResourceHookExecutor : IResourceHookContainer where T : { /// /// Checks whether a hook should be executed or not through reflective - /// verification if a hook is implemented on IResourceModel> + /// verification if a hook is implemented on IResourceModel /// /// true, if execute hook should be executed, false otherwise. /// The enum representing the type of hook. From 8af48d4b0c1d4dddb10d68cf5f3020b53746852b Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 9 Apr 2019 13:36:43 +0200 Subject: [PATCH 015/168] fix: naming rule violation --- .../Internal/Generics/GenericProcessorFactory.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs index 5baa9615bb..3eb8883c24 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs @@ -41,12 +41,12 @@ public GenericProcessorFactory(IScopedServiceProvider serviceProvider) } public TInterface GetProcessor(Type openGenericType, Type resourceType) - => _getProcessor(openGenericType, resourceType); + => GetProcessor(openGenericType, resourceType); public TInterface GetProcessor(Type openGenericType, Type resourceType, Type keyType) - => _getProcessor(openGenericType, resourceType, keyType); + => GetProcessor(openGenericType, resourceType, keyType); - private TInterface _getProcessor(Type openGenericType, params Type[] types) + private TInterface GetProcessor(Type openGenericType, params Type[] types) { var concreteType = openGenericType.MakeGenericType(types); From 035b6741b894d1b8e5ee6accca3805ffc4cddf08 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 9 Apr 2019 13:53:40 +0200 Subject: [PATCH 016/168] fix: edge case --- .../Internal/ImplementedResourceHooks.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs index 354d829685..35063902e9 100644 --- a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs +++ b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs @@ -55,7 +55,7 @@ void DiscoverImplementedHooksForModel() } else { - throw new JsonApiSetupException($@" Implemented hooks may be discovered only once. + throw new JsonApiSetupException($@"Implemented hooks may be discovered only once. Adding such implementations at runtime is currently not supported."); } @@ -66,9 +66,15 @@ void DiscoverImplementedHooksForModel() resourceDefinitionImplementationType = match; break; } + if (resourceDefinitionImplementationType != null) + { + ImplementedHooks = _allHooks.Where(h => resourceDefinitionImplementationType.GetMethod(h.ToString("G")).DeclaringType == resourceDefinitionImplementationType) + .ToArray(); + } else + { + ImplementedHooks = new ResourceHook[0]; + } - ImplementedHooks = _allHooks.Where(h => resourceDefinitionImplementationType.GetMethod(h.ToString("G")).DeclaringType == resourceDefinitionImplementationType) - .ToArray(); } } } From f2d446ad18f129d0aab07ff55c205e6352f0b625 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 9 Apr 2019 14:35:46 +0200 Subject: [PATCH 017/168] fix: previous rename caused dotnet to crash --- .../Internal/Generics/GenericProcessorFactory.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs index 3eb8883c24..f6849b178b 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs @@ -41,12 +41,12 @@ public GenericProcessorFactory(IScopedServiceProvider serviceProvider) } public TInterface GetProcessor(Type openGenericType, Type resourceType) - => GetProcessor(openGenericType, resourceType); + => GetProcessorInternal(openGenericType, resourceType); public TInterface GetProcessor(Type openGenericType, Type resourceType, Type keyType) - => GetProcessor(openGenericType, resourceType, keyType); + => GetProcessorInternal(openGenericType, resourceType, keyType); - private TInterface GetProcessor(Type openGenericType, params Type[] types) + private TInterface GetProcessorInternal(Type openGenericType, params Type[] types) { var concreteType = openGenericType.MakeGenericType(types); From 9a64aad8f8736d7c5aa152dd33024ea506c9fbb6 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 10 Apr 2019 14:59:22 +0200 Subject: [PATCH 018/168] feat: first version of generic tree traversing --- .../Internal/ImplementedResourceHooks.cs | 1 + .../Services/ResourceHookExecutor.cs | 240 +++++++++++++++++- test/UnitTests/Models/ResourceHookTests.cs | 209 ++++++++------- 3 files changed, 348 insertions(+), 102 deletions(-) diff --git a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs index 35063902e9..bd2d864d96 100644 --- a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs +++ b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs @@ -9,6 +9,7 @@ namespace JsonApiDotNetCore.Internal { public enum ResourceHook { + None, // https://stackoverflow.com/questions/24151354/is-it-a-good-practice-to-add-a-null-or-none-member-to-the-enum BeforeCreate, AfterCreate, BeforeRead, diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index e8a0b7d0ea..9f18f2cd62 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; + using System.Linq; using System.Reflection; using JsonApiDotNetCore.Internal; @@ -9,6 +10,8 @@ namespace JsonApiDotNetCore.Services { + + /// public class ResourceHookExecutor : IResourceHookExecutor where TEntity : class, IIdentifiable { @@ -17,12 +20,19 @@ public class ResourceHookExecutor : IResourceHookExecutor wher protected readonly IJsonApiContext _jsonApiContext; protected readonly IGenericProcessorFactory _genericProcessorFactory; protected readonly ResourceDefinition _resourceDefinition; + protected readonly IResourceGraph _graph; + protected readonly Type _entityType; + protected Dictionary>> _meta; + private ResourceHook _hookInTreeTraversal; public ResourceHookExecutor(IJsonApiContext jsonApiContext, IImplementedResourceHooks hooksConfiguration) { _genericProcessorFactory = jsonApiContext.GenericProcessorFactory; _jsonApiContext = jsonApiContext; _implementedHooks = hooksConfiguration.ImplementedHooks; + _graph = _jsonApiContext.ResourceGraph; + _entityType = typeof(TEntity); + _meta = new Dictionary>>(); } /// @@ -31,20 +41,51 @@ public virtual bool ShouldExecuteHook(ResourceHook hook) return _implementedHooks.Contains(hook); } - /// public virtual TEntity BeforeCreate(TEntity entity, ResourceAction actionSource) { if (!ShouldExecuteHook(ResourceHook.BeforeCreate)) return entity; - - throw new NotImplementedException(); + var hookContainer = GetResourceDefinition(_entityType); + /// traversing the 0th layer. Not including this in the recursive function + /// because the most complexities that arrise in the tree traversal do not + /// apply to the 0th layer (eg non-homogeneity of the next layers) + entity = hookContainer.BeforeCreate(entity, actionSource); // eg all of type {Article} + + /// We use IIdentifiable instead of TEntity, because deeper layers + /// in the tree traversal will not necessarily be homogenous (i.e. + /// not all elements will be some same type T). + /// eg: this list will be all of type {Article}, but deeper layers + /// could consist of { Tag, Author, Comment } + var currentLayer = new List() { entity }; + + /// gets the dictionary containing all (type) info we need for the + /// traversal of the next layer. We pass it ResourceHook.BeforeUpdate because + /// thats the hook we will be calling for the affected relations (see implementation overview). + var contextEntity = _graph.GetContextEntity(_entityType); + var meta = CreateOrUpdateMeta(currentLayer, ResourceHook.BeforeUpdate); + + BreadthFirstTraverseLayers(currentLayer, meta, (relatedEntities, correspondingContainer) => + { + // WILL BE AS SIMPLE AS: + // return correspondingContainer.BeforeUpdate(relatedEntities, actionSource); + + // BECAUSE OF TEntity vs IEnumerable discrepancies (due to one-to-one, one-to-many + var kak = relatedEntities.First(); + var poep = correspondingContainer.BeforeUpdate(kak, actionSource); + return new List() { poep }.AsEnumerable(); + }); + + return entity; } + + + /// public virtual TEntity AfterCreate(TEntity entity, ResourceAction actionSource) { if (!ShouldExecuteHook(ResourceHook.AfterCreate)) return entity; - - throw new NotImplementedException(); + var hookContainer = GetResourceDefinition(_entityType); + return hookContainer.AfterCreate(entity, actionSource); } /// @@ -83,7 +124,7 @@ public virtual TEntity AfterUpdate(TEntity entity, ResourceAction actionSource) public virtual void BeforeDelete(TEntity entity, ResourceAction actionSource) { if (!ShouldExecuteHook(ResourceHook.BeforeDelete)) return; - var hookContainer = GetResourceDefinition(typeof(TEntity)); + var hookContainer = GetResourceDefinition(_entityType); hookContainer.BeforeDelete(entity, actionSource); } @@ -91,12 +132,195 @@ public virtual void BeforeDelete(TEntity entity, ResourceAction actionSource) public virtual void AfterDelete(TEntity entity, bool succeeded, ResourceAction actionSource) { if (!ShouldExecuteHook(ResourceHook.AfterDelete)) return; - var hookContainer = GetResourceDefinition(typeof(TEntity)); + var hookContainer = GetResourceDefinition(_entityType); hookContainer.AfterDelete(entity, succeeded, actionSource); } + /// + /// PSUEDO CODE: + /// + /// Get all property infos where attribute statisfies isAssignable(RelationshipAttribute); + /// using JADNC resourcegraph: + /// list relatedentitytypes = getrelations(_entityType) + /// .filter( WhereIsPopulated() ) (?) + /// .filter( WhereHookForThatEntityIsImplemented() ) + /// ENTITYHOOKS = Get-all-hook-implementations-for(relatedentitytypes) + /// + /// ^^^^^ MakeDict{TRelationType, IHookExecutor{TRelationType} + /// + /// nextLayer = []; + /// for (currEntity in rootEntities); + /// { + /// currEntity.RelationA, currEntity.relation.B, currEntity.relationC + /// transform to + /// relations = [relationA, relationB, relationC] (relation_i where i = {A, B, C} + /// for (i in {A, B, C} ) + /// { + /// adjustedRelation_i = ENTITYHOOKS.hookForRelation_i(relation_i) + /// currentEntity.relation_i = adjustedRelation_i + /// if (filteredRelation_i != null) nextLayer.push relation_i + /// } + /// nextLayerMeta = MakeDict{typeof(TRelationType), IHookExecutor{TRelationType} + /// } + /// + /// Traverse(nextLayer, nextLayerMeta) + /// + void BreadthFirstTraverseLayers( + IEnumerable currentLayer, + Dictionary>> meta, + Func, IResourceHookContainer, IEnumerable> hookExecution + ) + { + var nextLayer = new List(); + foreach (IIdentifiable currentLayerEntity in currentLayer) + { + foreach (string metaKey in meta.Keys) + { + (var attr, var hookContainer) = meta[metaKey]; + /// because currentLayer is not type-homogeneous (which is + /// why we need to use IIdentifiable for the list type of + /// that layer), we need to check if relatedType is really + /// related to parentType. We do this through comparison of Metakey + string requiredMetaKey = CreateMetaKey(attr.Type, currentLayerEntity.GetType()); + if (metaKey != requiredMetaKey) continue; + var relatedEntities = attr.GetValue(currentLayerEntity); + if (!(relatedEntities is IEnumerable)) + { + // actually need to create list instead of casting here + // this will break on runtime, but will work for precompilation checks (just put it like this for quick development for now) + relatedEntities = relatedEntities as IEnumerable; + } + relatedEntities = hookExecution(relatedEntities as IEnumerable, hookContainer); + attr.SetValue(currentLayerEntity, relatedEntities); + + // @TODO distinguish between collections (check for length) + // and to one relations (check for null). + if (relatedEntities != null) nextLayer.Concat(relatedEntities as IEnumerable); + } + } + + // consider making a hard killswitch based on depth number + if (nextLayer.Count > 0) + { + /// there might be new relation types, so we need to check for that + /// and update our metadict accordingly. + var updatedMeta = CreateOrUpdateMeta(nextLayer); + BreadthFirstTraverseLayers(nextLayer, meta, hookExecution); + } + } + + /// + /// Creates a (helper) dictionary containing meta information needed for + /// the traversal of the next layer. It contains as + /// keys: Type, namely typeof(TRelatedType) that will occur in the traversal + /// of the next layer, + /// values: a Tuple of + /// * RelationshipAttribute (that contains getters and setters) + /// * IResourceHookExecutor{TRelatedType} to access the actual (nested) hook + /// + /// The meta dict. + /// List of entities of in the current layer + /// The target resource hook type + Dictionary>> + CreateOrUpdateMeta( + IEnumerable nextLayer, + ResourceHook hook = ResourceHook.None) + { + var types = GetUniqueConcreteTypes(nextLayer); + _hookInTreeTraversal = _hookInTreeTraversal != + ResourceHook.None ? + _hookInTreeTraversal : + hook; + foreach (Type targetType in types) + { + var contextEntity = _graph.GetContextEntity(targetType); + var metaDictForTargetType = contextEntity.Relationships.ToDictionary( + attr => CreateMetaKey(attr.Type, targetType), + attr => CreateTuple(attr)); + /// keep only the meta info we really need for the traversal of the next layer + /// also remove duplicates (this is an inefficient implementation of meta cache). + PruneMetaDictionary(metaDictForTargetType, _hookInTreeTraversal); + _meta = _meta.Concat(metaDictForTargetType) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } + return _meta; + } + + /// + /// Gets a unique list of all the concrete IIdentifiable types within a layer. + /// + /// The unique concrete types. + /// Layer. + Type[] GetUniqueConcreteTypes(IEnumerable layer) + { + return new HashSet(layer.Select(e => e.GetType())).ToArray(); + } + + Tuple> CreateTuple(RelationshipAttribute attr) + { + var hookContainer = (IResourceHookContainer)GetResourceDefinition(attr.Type); + return new Tuple>(attr, hookContainer); + } + + /// + /// Creates the key for the meta dict. The RelationshipAttribute that is + /// in the value of the meta dict is specific for a particular related type + /// AS WELL AS parent type. This is reflected by the format of the meta key. + /// + /// The meta key. + /// Related type. + /// Parent type. + string CreateMetaKey(Type relatedType, Type parentType) + { + string newKey = $"{relatedType.Name}-{parentType.Name}"; + if (_meta.ContainsKey(newKey)) + { + return $"DUPLICATE-{Guid.NewGuid()}"; + } + return newKey; + } + + /// + /// Gets rid of keys in the meta dict that won't be needed for the next layer. + /// + /// It does so by: + /// 1) checking if there was at all a IResourceHookExecutor + /// implemented for this type (ResourceDefinition by default); + /// 2) then checking if there is a implementation of the particular + /// target hook. + /// @TODO part (2) still needs to be implemented: + /// => get a hold of IImplementedHooks for that particular type + /// => or just hookexecutor for that type and call public ShouldExecute method. + /// @TODO We need to allow pruning of meta dict using relationship strings, + /// which can be relevant if we have eg ?include=.. params that we only care about + /// also. This becomes important for performance when we have a model with relation amount + /// n >> 0, and inclusion count i ~ 0. + /// + /// investigate: maybe value is null for a not included type instead of an empty list, + /// and maybe there is an empty list when a relation is included but there were no records. + /// if this is true, we can implement all of it a lot more efficient without using more reflection. + /// + /// + /// the meta dictionary + void PruneMetaDictionary( + Dictionary>> meta, + ResourceHook targetHook) + { + var dupes = meta.Where(pair => pair.Key.Contains("DUPLICATE")).Select(pair => pair.Key); + foreach (string target in dupes) + { + meta.Remove(target); + } + + var noHookImplementation = meta.Where(pair => pair.Value.Item2 == null) // do something with ShouldExecute(targethook) for related type. + .Select(pair => pair.Key); + foreach (string target in noHookImplementation) + { + meta.Remove(target); + } + } - private IResourceHookContainer GetResourceDefinition(Type targetEntity) + IResourceHookContainer GetResourceDefinition(Type targetEntity) { return (IResourceHookContainer)_genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); } diff --git a/test/UnitTests/Models/ResourceHookTests.cs b/test/UnitTests/Models/ResourceHookTests.cs index 43df21ea4d..c8db60f1d9 100644 --- a/test/UnitTests/Models/ResourceHookTests.cs +++ b/test/UnitTests/Models/ResourceHookTests.cs @@ -8,6 +8,8 @@ using System; using System.Collections.Generic; using Xunit; +using Dummy = UnitTests.Models.AllHooksWithRelations.Dummy; +using DummyResourceDefinition = UnitTests.Models.AllHooksWithRelations.DummyResourceDefinition; namespace UnitTests.Models { @@ -20,13 +22,14 @@ public ResourceHooks_Tests() // Build() exposes the static ResourceGraphBuilder.Instance member, which // is consumed by ResourceDefinition class. new ResourceGraphBuilder().Build(); - } [Fact] public void Hook_Discovery() { - var hookConfig = new ImplementedResourceHooks(); + // arrange & act + var hookConfig = new ImplementedResourceHooks(); + // assert Assert.Contains(ResourceHook.BeforeDelete, hookConfig.ImplementedHooks); Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); Assert.Equal(2, hookConfig.ImplementedHooks.Length); @@ -35,91 +38,101 @@ public void Hook_Discovery() [Fact] public void BeforeCreate_Hook_Is_Called() { - (var resourceDefinitionMock, var contextMock) = CreateMocks(); - - var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); - hookExecutor.BeforeCreate(It.IsAny(), It.IsAny()); - resourceDefinitionMock.Verify(rd => rd.BeforeCreate(It.IsAny(), It.IsAny()), Times.Once()); + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + // act + hookExecutor.BeforeCreate(It.IsAny(), It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.BeforeCreate(It.IsAny(), It.IsAny()), Times.Once()); } [Fact] public void AfterCreate_Hook_Is_Called() { - (var resourceDefinitionMock, var contextMock) = CreateMocks(); - - var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); - hookExecutor.AfterCreate(It.IsAny(), It.IsAny()); - resourceDefinitionMock.Verify(rd => rd.AfterCreate(It.IsAny(), It.IsAny()), Times.Once()); + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + // act + hookExecutor.AfterCreate(It.IsAny(), It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.AfterCreate(It.IsAny(), It.IsAny()), Times.Once()); } [Fact] public void BeforeRead_Hook_Is_Called() { - (var resourceDefinitionMock, var contextMock) = CreateMocks(); - - var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + // act hookExecutor.BeforeRead(It.IsAny()); + // assert resourceDefinitionMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); } [Fact] public void AfterRead_Hook_Is_Called() { - (var resourceDefinitionMock, var contextMock) = CreateMocks(); - - var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); - hookExecutor.AfterRead(It.IsAny>(), It.IsAny()); - resourceDefinitionMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + // act + hookExecutor.AfterRead(It.IsAny>(), It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void BeforeUpdate_Hook_Is_Called() { - (var resourceDefinitionMock, var contextMock) = CreateMocks(); - - var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); - hookExecutor.BeforeUpdate(It.IsAny(), It.IsAny()); - resourceDefinitionMock.Verify(rd => rd.BeforeUpdate(It.IsAny(), It.IsAny()), Times.Once()); + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + // act + hookExecutor.BeforeUpdate(It.IsAny(), It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.BeforeUpdate(It.IsAny(), It.IsAny()), Times.Once()); } [Fact] public void AfterUpdate_Hook_Is_Called() { - (var resourceDefinitionMock, var contextMock) = CreateMocks(); - - var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); - hookExecutor.AfterUpdate(It.IsAny(), It.IsAny()); - resourceDefinitionMock.Verify(rd => rd.AfterUpdate(It.IsAny(), It.IsAny()), Times.Once()); + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + // act + hookExecutor.AfterUpdate(It.IsAny(), It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.AfterUpdate(It.IsAny(), It.IsAny()), Times.Once()); } [Fact] public void BeforeDelete_Hook_Is_Called() { - (var resourceDefinitionMock, var contextMock) = CreateMocks(); + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + + // act + hookExecutor.BeforeDelete(It.IsAny(), It.IsAny()); - var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); - hookExecutor.BeforeDelete(It.IsAny(), It.IsAny()); - resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny(), It.IsAny()), Times.Once()); + // assert + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny(), It.IsAny()), Times.Once()); } [Fact] public void AfterDelete_Hook_Is_Called() { - (var resourceDefinitionMock, var contextMock) = CreateMocks(); - - var hookExecutor = new ResourceHookExecutor(contextMock.Object, new ImplementedResourceHooks()); - hookExecutor.AfterDelete(It.IsAny(), true, It.IsAny()); - resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny(), true, It.IsAny()), Times.Once()); + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + // act + hookExecutor.AfterDelete(It.IsAny(), true, It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny(), true, It.IsAny()), Times.Once()); } - Mock CreateResourceDefinitionMock() + Mock CreateResourceDefinitionMock() { - var resourceDefinition = new Mock(); - resourceDefinition.Setup(rd => rd.BeforeCreate(It.IsAny(), It.IsAny())); - resourceDefinition.Setup(rd => rd.AfterCreate(It.IsAny(), It.IsAny())); + var resourceDefinition = new Mock(); + resourceDefinition.Setup(rd => rd.BeforeCreate(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterCreate(It.IsAny(), It.IsAny())); resourceDefinition.Setup(rd => rd.BeforeRead(It.IsAny(), null)); - resourceDefinition.Setup(rd => rd.AfterRead(It.IsAny>(), It.IsAny())); - resourceDefinition.Setup(rd => rd.BeforeUpdate(It.IsAny(), It.IsAny())); - resourceDefinition.Setup(rd => rd.AfterUpdate(It.IsAny(), It.IsAny())); - resourceDefinition.Setup(rd => rd.BeforeDelete(It.IsAny(), It.IsAny())); - resourceDefinition.Setup(rd => rd.AfterDelete(It.IsAny(), It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterRead(It.IsAny>(), It.IsAny())); + resourceDefinition.Setup(rd => rd.BeforeUpdate(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterUpdate(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.BeforeDelete(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterDelete(It.IsAny(), It.IsAny(), It.IsAny())); return resourceDefinition; } - (Mock, Mock) CreateMocks() + (Mock, Mock, IResourceHookExecutor) CreateTestObjects() { var resourceDefinition = CreateResourceDefinitionMock(); @@ -130,62 +143,70 @@ Mock CreateResourceDefinitionMock() var context = new Mock(); context.Setup(c => c.GenericProcessorFactory).Returns(processorFactory.Object); - return (resourceDefinition, context); + var hookExecutor = new ResourceHookExecutor(context.Object, new ImplementedResourceHooks()); + + return (resourceDefinition, context, hookExecutor); } - } - public class Dummy : Identifiable - { } - - public class DummyResourceDefinition : ResourceDefinition + public class PartialHooks { - public override void BeforeDelete(Dummy entity, ResourceAction actionSource) + public class Dummy : Identifiable { + } - public override void AfterDelete(Dummy entity, bool succeeded, ResourceAction actionSource) + public class DummyResourceDefinition : ResourceDefinition { + public override void BeforeDelete(Dummy entity, ResourceAction actionSource) + { + } + public override void AfterDelete(Dummy entity, bool succeeded, ResourceAction actionSource) + { + } } } - - public class DummyWithAllHooks : Identifiable - { - - } - - public class DummyWithAllHooksResourceDefinition : ResourceDefinition + public class AllHooksWithRelations { - public override DummyWithAllHooks BeforeCreate(DummyWithAllHooks entity, ResourceAction actionSource) - { - return entity; - } - public override DummyWithAllHooks AfterCreate(DummyWithAllHooks entity, ResourceAction actionSource) - { - return entity; - } - public override void BeforeRead(ResourceAction actionSource, string stringId = null) - { - - } - public override IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) - { - return entities; - } - public override DummyWithAllHooks BeforeUpdate(DummyWithAllHooks entity, ResourceAction actionSource) - { - return entity; - } - public override DummyWithAllHooks AfterUpdate(DummyWithAllHooks entity, ResourceAction actionSource) - { - return entity; - } - public override void BeforeDelete(DummyWithAllHooks entity, ResourceAction actionSource) - { - } - public override void AfterDelete(DummyWithAllHooks entity, bool succeeded, ResourceAction actionSource) - { - + public class Dummy : Identifiable + { + + } + + public class DummyResourceDefinition : ResourceDefinition + { + public override Dummy BeforeCreate(Dummy entity, ResourceAction actionSource) + { + return entity; + } + public override Dummy AfterCreate(Dummy entity, ResourceAction actionSource) + { + return entity; + } + public override void BeforeRead(ResourceAction actionSource, string stringId = null) + { + + } + public override IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) + { + return entities; + } + public override Dummy BeforeUpdate(Dummy entity, ResourceAction actionSource) + { + return entity; + } + public override Dummy AfterUpdate(Dummy entity, ResourceAction actionSource) + { + return entity; + } + public override void BeforeDelete(Dummy entity, ResourceAction actionSource) + { + } + public override void AfterDelete(Dummy entity, bool succeeded, ResourceAction actionSource) + { + + } } } + } From 690aa02bb7ed4e76009e01e68b60efb4106de43b Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Fri, 12 Apr 2019 14:01:14 +0200 Subject: [PATCH 019/168] feat: more performant version of traversal --- src/JsonApiDotNetCore/Internal/TypeHelper.cs | 16 +- .../Services/IResourceHookExecutor.cs | 73 +-- .../Services/ResourceHookExecutor.cs | 463 ++++++------------ .../Services/ResourceHookMetaInfo.cs | 169 +++++++ 4 files changed, 360 insertions(+), 361 deletions(-) create mode 100644 src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs diff --git a/src/JsonApiDotNetCore/Internal/TypeHelper.cs b/src/JsonApiDotNetCore/Internal/TypeHelper.cs index 0a3e01d0d1..454741ff03 100644 --- a/src/JsonApiDotNetCore/Internal/TypeHelper.cs +++ b/src/JsonApiDotNetCore/Internal/TypeHelper.cs @@ -73,8 +73,7 @@ public static T ConvertType(object value) /// Collection of concrete type public static IList ConvertListType(IEnumerable values, Type type) { - var listType = typeof(List<>).MakeGenericType(type); - IList list = (IList)Activator.CreateInstance(listType); + var list = CreateListFor(type); foreach (var value in values) { list.Add(ConvertType(value, type)); @@ -82,5 +81,18 @@ public static IList ConvertListType(IEnumerable values, Type type) return list; } + + /// + /// Reflectively nstantiates a list of a certain type. + /// + /// The list of the target type + /// The target type + public static IList CreateListFor(Type type) + { + var boundListType = typeof(List<>).MakeGenericType(type); + IList list = (IList)Activator.CreateInstance(boundListType); + return list; + + } } } diff --git a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs index 0b82bbc4de..9fd1bd9d03 100644 --- a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs @@ -21,17 +21,7 @@ public enum ResourceAction } - /// - /// An interface that should be implemented by the class that defines resource hooks. - /// - /// By default, the ResourceDefinition class inherits - /// from IResourceHookContainer, which means that the - /// hooks can be implemented on ResourceDefinition. - /// - /// See IResourceHookExecutor for details on the - /// resource hook execution flow. - /// - public interface IResourceHookContainer where T : class, IIdentifiable + public interface IResourceHookBase where T : class, IIdentifiable { /// /// A hook executed before creating an entity. Can be used eg. for authorization. @@ -41,10 +31,10 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// This hook is executed from: /// * EntityResourceService.CreateAsync /// - /// The (adjusted) entity to be created - /// The entity to be created + /// The (adjusted) entities to be created + /// The entities to be created /// The pipeline from which the hook was called - T BeforeCreate(T entity, ResourceAction actionSource); + IEnumerable BeforeCreate(IEnumerable entities, ResourceAction actionSource); /// /// A hook executed after creating an entity. Can be used eg. for publishing events. @@ -52,9 +42,9 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// This hook is executed from: /// * EntityResourceService.CreateAsync /// - /// The entity that was created + /// The entities that were created /// The pipeline from which the hook was called - T AfterCreate(T entity, ResourceAction actionSource); + IEnumerable AfterCreate(IEnumerable entities, ResourceAction actionSource); /// /// A hook executed after before reading entities. Can be used eg. for logging, authorization. @@ -100,10 +90,10 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// This hook is executed from: /// * EntityResourceService.UpdateAsync /// - /// The (adjusted) entity to be updated - /// The entity to be updated + /// The (adjusted) entities to be updated + /// The entities to be updated /// The pipeline from which the hook was called - T BeforeUpdate(T entity, ResourceAction actionSource); + IEnumerable BeforeUpdate(IEnumerable entities, ResourceAction actionSource); /// /// A hook executed after updating an entity. Can be used eg. for publishing an event. @@ -111,27 +101,50 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// This hook is executed from: /// * EntityResourceService.UpdateAsync /// - /// The entity that was updated + /// The entities that were updated /// The pipeline from which the hook was called - T AfterUpdate(T entity, ResourceAction actionSource); + IEnumerable AfterUpdate(IEnumerable entities, ResourceAction actionSource); /// /// A hook executed before deleting an entity. Can be used eg. for authorization. /// - /// The entity to be deleted + /// The entities to be deleted /// The pipeline from which the hook was called - void BeforeDelete(T entity, ResourceAction actionSource); + void BeforeDelete(IEnumerable entities, ResourceAction actionSource); /// /// A hook executed before deleting an entity. Can be used eg. for publishing an event. /// - /// The entity to be deleted + /// The entities to be deleted /// The pipeline from which the hook was called /// A boolean to indicate whether the deletion was succesful - void AfterDelete(T entity, bool succeeded, ResourceAction actionSource); + void AfterDelete(IEnumerable entities, bool succeeded, ResourceAction actionSource); + } + + + /// + /// An interface that should be implemented by the class that defines resource hooks. + /// + /// By default, the ResourceDefinition class inherits + /// from IResourceHookContainer, which means that the + /// hooks can be implemented on ResourceDefinition. + /// + /// See IResourceHookExecutor for details on the + /// resource hook execution flow. + /// + public interface IResourceHookContainer : IResourceHookBase where T : class, IIdentifiable + { + /// + /// Checks whether a hook should be executed or not through reflective + /// verification if a hook is implemented on IResourceModel + /// + /// true, if execute hook should be executed, false otherwise. + /// The enum representing the type of hook. + bool ShouldExecuteHook(ResourceHook hook); } + /// /// A utility class responsible for executing hooks as defined in /// the IResourceHookContainer. @@ -149,14 +162,8 @@ public interface IResourceHookContainer where T : class, IIdentifiable /// of service{Model}.GetAsync() with a nested include of related entities, ResourceDefintions for all /// involved models are resolved and used in a more complex travesal of the resultset. /// - public interface IResourceHookExecutor : IResourceHookContainer where T : class, IIdentifiable + public interface IResourceHookExecutor : IResourceHookBase where T : class, IIdentifiable { - /// - /// Checks whether a hook should be executed or not through reflective - /// verification if a hook is implemented on IResourceModel - /// - /// true, if execute hook should be executed, false otherwise. - /// The enum representing the type of hook. - bool ShouldExecuteHook(ResourceHook hook); + } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index 9f18f2cd62..651a0d3d8b 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -1,13 +1,11 @@ using System; -using System.Collections; using System.Collections.Generic; - using System.Linq; -using System.Reflection; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; + namespace JsonApiDotNetCore.Services { @@ -15,83 +13,69 @@ namespace JsonApiDotNetCore.Services /// public class ResourceHookExecutor : IResourceHookExecutor where TEntity : class, IIdentifiable { - protected readonly ResourceHook[] _implementedHooks; protected readonly IJsonApiContext _jsonApiContext; protected readonly IGenericProcessorFactory _genericProcessorFactory; protected readonly ResourceDefinition _resourceDefinition; protected readonly IResourceGraph _graph; protected readonly Type _entityType; - protected Dictionary>> _meta; + protected readonly IResourceHookMetaInfo _meta; private ResourceHook _hookInTreeTraversal; - public ResourceHookExecutor(IJsonApiContext jsonApiContext, IImplementedResourceHooks hooksConfiguration) + + public ResourceHookExecutor( + IJsonApiContext jsonApiContext, + IImplementedResourceHooks hooksConfiguration, + IResourceHookMetaInfo meta + ) { _genericProcessorFactory = jsonApiContext.GenericProcessorFactory; _jsonApiContext = jsonApiContext; - _implementedHooks = hooksConfiguration.ImplementedHooks; _graph = _jsonApiContext.ResourceGraph; + _meta = meta; + _implementedHooks = hooksConfiguration.ImplementedHooks; _entityType = typeof(TEntity); - _meta = new Dictionary>>(); } - /// - public virtual bool ShouldExecuteHook(ResourceHook hook) - { - return _implementedHooks.Contains(hook); - } - public virtual TEntity BeforeCreate(TEntity entity, ResourceAction actionSource) + public virtual IEnumerable BeforeCreate(IEnumerable entities, ResourceAction actionSource) { - if (!ShouldExecuteHook(ResourceHook.BeforeCreate)) return entity; - var hookContainer = GetResourceDefinition(_entityType); + var hookContainer = _meta.GetResourceDefinition(ResourceHook.BeforeCreate); + if (hookContainer == null) return entities; /// traversing the 0th layer. Not including this in the recursive function /// because the most complexities that arrise in the tree traversal do not /// apply to the 0th layer (eg non-homogeneity of the next layers) - entity = hookContainer.BeforeCreate(entity, actionSource); // eg all of type {Article} + entities = hookContainer.BeforeCreate(entities, actionSource); // eg all of type {Article} /// We use IIdentifiable instead of TEntity, because deeper layers /// in the tree traversal will not necessarily be homogenous (i.e. /// not all elements will be some same type T). /// eg: this list will be all of type {Article}, but deeper layers /// could consist of { Tag, Author, Comment } - var currentLayer = new List() { entity }; - - /// gets the dictionary containing all (type) info we need for the - /// traversal of the next layer. We pass it ResourceHook.BeforeUpdate because - /// thats the hook we will be calling for the affected relations (see implementation overview). - var contextEntity = _graph.GetContextEntity(_entityType); - var meta = CreateOrUpdateMeta(currentLayer, ResourceHook.BeforeUpdate); - - BreadthFirstTraverseLayers(currentLayer, meta, (relatedEntities, correspondingContainer) => + _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.BeforeUpdate); + BreadthFirstTraverse(entities, (container, relatedEntities) => { - // WILL BE AS SIMPLE AS: - // return correspondingContainer.BeforeUpdate(relatedEntities, actionSource); - - // BECAUSE OF TEntity vs IEnumerable discrepancies (due to one-to-one, one-to-many - var kak = relatedEntities.First(); - var poep = correspondingContainer.BeforeUpdate(kak, actionSource); - return new List() { poep }.AsEnumerable(); + return container.BeforeUpdate(relatedEntities, actionSource); }); - return entity; + return entities; } - - - /// - public virtual TEntity AfterCreate(TEntity entity, ResourceAction actionSource) + public virtual IEnumerable AfterCreate(IEnumerable entities, ResourceAction actionSource) { - if (!ShouldExecuteHook(ResourceHook.AfterCreate)) return entity; - var hookContainer = GetResourceDefinition(_entityType); - return hookContainer.AfterCreate(entity, actionSource); + var hookContainer = _meta.GetResourceDefinition(ResourceHook.AfterCreate); + if (hookContainer == null) return entities; + + return hookContainer.AfterCreate(entities, actionSource); } /// public virtual void BeforeRead(ResourceAction actionSource, string stringId = null) { - if (!ShouldExecuteHook(ResourceHook.BeforeRead)) return; + var hookContainer = _meta.GetResourceDefinition(ResourceHook.BeforeRead); + if (hookContainer == null) return; + throw new NotImplementedException(); } @@ -99,342 +83,169 @@ public virtual void BeforeRead(ResourceAction actionSource, string stringId = nu /// public virtual IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) { - if (!ShouldExecuteHook(ResourceHook.AfterRead)) return entities; + var hookContainer = _meta.GetResourceDefinition(ResourceHook.AfterRead); + if (hookContainer == null) return entities; + throw new NotImplementedException(); } /// - public virtual TEntity BeforeUpdate(TEntity entity, ResourceAction actionSource) + public virtual IEnumerable BeforeUpdate(IEnumerable entities, ResourceAction actionSource) { - if (!ShouldExecuteHook(ResourceHook.BeforeUpdate)) return entity; + var hookContainer = _meta.GetResourceDefinition(ResourceHook.BeforeUpdate); + if (hookContainer == null) return entities; + throw new NotImplementedException(); } /// - public virtual TEntity AfterUpdate(TEntity entity, ResourceAction actionSource) + public virtual IEnumerable AfterUpdate(IEnumerable entities, ResourceAction actionSource) { - if (!ShouldExecuteHook(ResourceHook.AfterUpdate)) return entity; + var hookContainer = _meta.GetResourceDefinition(ResourceHook.AfterUpdate); + if (hookContainer == null) return entities; + throw new NotImplementedException(); } /// - public virtual void BeforeDelete(TEntity entity, ResourceAction actionSource) + public virtual void BeforeDelete(IEnumerable entities, ResourceAction actionSource) { - if (!ShouldExecuteHook(ResourceHook.BeforeDelete)) return; - var hookContainer = GetResourceDefinition(_entityType); - hookContainer.BeforeDelete(entity, actionSource); + var hookContainer = _meta.GetResourceDefinition(ResourceHook.BeforeDelete); + if (hookContainer == null) return; + + hookContainer.BeforeDelete(entities, actionSource); } /// - public virtual void AfterDelete(TEntity entity, bool succeeded, ResourceAction actionSource) + public virtual void AfterDelete(IEnumerable entities, bool succeeded, ResourceAction actionSource) { - if (!ShouldExecuteHook(ResourceHook.AfterDelete)) return; - var hookContainer = GetResourceDefinition(_entityType); - hookContainer.AfterDelete(entity, succeeded, actionSource); + var hookContainer = _meta.GetResourceDefinition(ResourceHook.AfterDelete); + if (hookContainer == null) return; + + hookContainer.AfterDelete(entities, succeeded, actionSource); } - /// - /// PSUEDO CODE: - /// - /// Get all property infos where attribute statisfies isAssignable(RelationshipAttribute); - /// using JADNC resourcegraph: - /// list relatedentitytypes = getrelations(_entityType) - /// .filter( WhereIsPopulated() ) (?) - /// .filter( WhereHookForThatEntityIsImplemented() ) - /// ENTITYHOOKS = Get-all-hook-implementations-for(relatedentitytypes) - /// - /// ^^^^^ MakeDict{TRelationType, IHookExecutor{TRelationType} - /// - /// nextLayer = []; - /// for (currEntity in rootEntities); - /// { - /// currEntity.RelationA, currEntity.relation.B, currEntity.relationC - /// transform to - /// relations = [relationA, relationB, relationC] (relation_i where i = {A, B, C} - /// for (i in {A, B, C} ) - /// { - /// adjustedRelation_i = ENTITYHOOKS.hookForRelation_i(relation_i) - /// currentEntity.relation_i = adjustedRelation_i - /// if (filteredRelation_i != null) nextLayer.push relation_i - /// } - /// nextLayerMeta = MakeDict{typeof(TRelationType), IHookExecutor{TRelationType} - /// } - /// - /// Traverse(nextLayer, nextLayerMeta) - /// - void BreadthFirstTraverseLayers( + void BreadthFirstTraverse( IEnumerable currentLayer, - Dictionary>> meta, - Func, IResourceHookContainer, IEnumerable> hookExecution + Func, IEnumerable, IEnumerable> hookExecutionAction ) { - var nextLayer = new List(); - foreach (IIdentifiable currentLayerEntity in currentLayer) - { - foreach (string metaKey in meta.Keys) - { - (var attr, var hookContainer) = meta[metaKey]; - /// because currentLayer is not type-homogeneous (which is - /// why we need to use IIdentifiable for the list type of - /// that layer), we need to check if relatedType is really - /// related to parentType. We do this through comparison of Metakey - string requiredMetaKey = CreateMetaKey(attr.Type, currentLayerEntity.GetType()); - if (metaKey != requiredMetaKey) continue; - var relatedEntities = attr.GetValue(currentLayerEntity); - if (!(relatedEntities is IEnumerable)) - { - // actually need to create list instead of casting here - // this will break on runtime, but will work for precompilation checks (just put it like this for quick development for now) - relatedEntities = relatedEntities as IEnumerable; - } - relatedEntities = hookExecution(relatedEntities as IEnumerable, hookContainer); - attr.SetValue(currentLayerEntity, relatedEntities); - - // @TODO distinguish between collections (check for length) - // and to one relations (check for null). - if (relatedEntities != null) nextLayer.Concat(relatedEntities as IEnumerable); - } - } + // for the entities in the current layer: get the collection of all related entities + var relationshipsInCurrentLayer = ExtractionLoop(currentLayer); + // for the unique set of entities in that collection, execute the hooks + ExecutionLoop(relationshipsInCurrentLayer, hookExecutionAction); + // for the entities in the current layer: reassign relationships where needed. + AssignmentLoop(currentLayer, relationshipsInCurrentLayer); - // consider making a hard killswitch based on depth number - if (nextLayer.Count > 0) + var nextLayer = relationshipsInCurrentLayer.Values.SelectMany(entities => entities); + if (nextLayer.Any()) { - /// there might be new relation types, so we need to check for that - /// and update our metadict accordingly. - var updatedMeta = CreateOrUpdateMeta(nextLayer); - BreadthFirstTraverseLayers(nextLayer, meta, hookExecution); + var uniqueTypesInNextLayer = relationshipsInCurrentLayer.Keys.Select(k => k.Type); + _meta.UpdateMetaInformation(uniqueTypesInNextLayer); + BreadthFirstTraverse(nextLayer, hookExecutionAction); } } /// - /// Creates a (helper) dictionary containing meta information needed for - /// the traversal of the next layer. It contains as - /// keys: Type, namely typeof(TRelatedType) that will occur in the traversal - /// of the next layer, - /// values: a Tuple of - /// * RelationshipAttribute (that contains getters and setters) - /// * IResourceHookExecutor{TRelatedType} to access the actual (nested) hook + /// * Iterates through the entities in the current. This layer can be inhomogeneous. + /// * For each of these entities: gets all related entity for which we want to + /// execute a hook (target entities), this is defined in MetaInfo. + /// * Grouped per relation, stores these target in relationshipsInCurrentLayer /// - /// The meta dict. - /// List of entities of in the current layer - /// The target resource hook type - Dictionary>> - CreateOrUpdateMeta( - IEnumerable nextLayer, - ResourceHook hook = ResourceHook.None) + /// Hook targets for current layer. + /// Current layer. + Dictionary> ExtractionLoop( + IEnumerable currentLayer + ) { - var types = GetUniqueConcreteTypes(nextLayer); - _hookInTreeTraversal = _hookInTreeTraversal != - ResourceHook.None ? - _hookInTreeTraversal : - hook; - foreach (Type targetType in types) + var relationshipsInCurrentLayer = new Dictionary>(); + foreach (IIdentifiable currentLayerEntity in currentLayer) { - var contextEntity = _graph.GetContextEntity(targetType); - var metaDictForTargetType = contextEntity.Relationships.ToDictionary( - attr => CreateMetaKey(attr.Type, targetType), - attr => CreateTuple(attr)); - /// keep only the meta info we really need for the traversal of the next layer - /// also remove duplicates (this is an inefficient implementation of meta cache). - PruneMetaDictionary(metaDictForTargetType, _hookInTreeTraversal); - _meta = _meta.Concat(metaDictForTargetType) - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); - } - return _meta; - } - - /// - /// Gets a unique list of all the concrete IIdentifiable types within a layer. - /// - /// The unique concrete types. - /// Layer. - Type[] GetUniqueConcreteTypes(IEnumerable layer) - { - return new HashSet(layer.Select(e => e.GetType())).ToArray(); - } + foreach (RelationshipAttribute attr in _meta.GetMetaEntries(currentLayerEntity)) + { + var relationshipValue = attr.GetValue(currentLayerEntity); + if (!(relationshipValue is IEnumerable)) + { + // 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(relationshipValue.GetType()); + list.Add(relationshipValue); + relationshipValue = list; + } + var relatedEntities = relationshipValue as IEnumerable; + if (!relationshipsInCurrentLayer.ContainsKey(attr)) + { + relationshipsInCurrentLayer[attr] = relatedEntities; + } + else + { + relationshipsInCurrentLayer[attr].Concat(relatedEntities); + } + } - Tuple> CreateTuple(RelationshipAttribute attr) - { - var hookContainer = (IResourceHookContainer)GetResourceDefinition(attr.Type); - return new Tuple>(attr, hookContainer); + } + return relationshipsInCurrentLayer; } /// - /// Creates the key for the meta dict. The RelationshipAttribute that is - /// in the value of the meta dict is specific for a particular related type - /// AS WELL AS parent type. This is reflected by the format of the meta key. + /// Executes the hooks for every key in relationshipsInCurrentLayer, /// - /// The meta key. - /// Related type. - /// Parent type. - string CreateMetaKey(Type relatedType, Type parentType) + /// Hook targets for current layer. + /// Hook execution. + void ExecutionLoop( + Dictionary> relationshipsInCurrentLayer, + Func, IEnumerable, IEnumerable> hookExecution + ) { - string newKey = $"{relatedType.Name}-{parentType.Name}"; - if (_meta.ContainsKey(newKey)) + foreach (var pair in relationshipsInCurrentLayer) { - return $"DUPLICATE-{Guid.NewGuid()}"; + var attr = pair.Key; + var uniqueEntities = new HashSet(pair.Value); + var executor = _meta.GetResourceDefinition(attr.Type); + var filteredUniqueEntites = hookExecution(executor, uniqueEntities); + relationshipsInCurrentLayer[pair.Key] = filteredUniqueEntites.ToArray(); } - return newKey; } /// - /// Gets rid of keys in the meta dict that won't be needed for the next layer. - /// - /// It does so by: - /// 1) checking if there was at all a IResourceHookExecutor - /// implemented for this type (ResourceDefinition by default); - /// 2) then checking if there is a implementation of the particular - /// target hook. - /// @TODO part (2) still needs to be implemented: - /// => get a hold of IImplementedHooks for that particular type - /// => or just hookexecutor for that type and call public ShouldExecute method. - /// @TODO We need to allow pruning of meta dict using relationship strings, - /// which can be relevant if we have eg ?include=.. params that we only care about - /// also. This becomes important for performance when we have a model with relation amount - /// n >> 0, and inclusion count i ~ 0. - /// - /// investigate: maybe value is null for a not included type instead of an empty list, - /// and maybe there is an empty list when a relation is included but there were no records. - /// if this is true, we can implement all of it a lot more efficient without using more reflection. - /// + /// When this method is called, the values in relationshipsInCurrentLayer + /// will contain a subset compared to in the DoExtractionLoop call. + /// We now need to iterate through currentLayer again and remove any of + /// their related entities that do not occur in relationshipsInCurrentLayer /// - /// the meta dictionary - void PruneMetaDictionary( - Dictionary>> meta, - ResourceHook targetHook) + /// Current layer. + /// Hook targets for current layer. + void AssignmentLoop( + IEnumerable currentLayer, + Dictionary> relationshipsInCurrentLayer + ) { - var dupes = meta.Where(pair => pair.Key.Contains("DUPLICATE")).Select(pair => pair.Key); - foreach (string target in dupes) + // @TODO IM NOT EVEN SURE IF WE NEED TO REASSIGN ?! + // if we adjust same objects in memory, we should only be required + // to perform filter check + foreach (IIdentifiable currentLayerEntity in currentLayer) { - meta.Remove(target); - } + foreach (RelationshipAttribute attr in _meta.GetMetaEntries(currentLayerEntity)) + { + var parsedEntities = relationshipsInCurrentLayer[attr]; - var noHookImplementation = meta.Where(pair => pair.Value.Item2 == null) // do something with ShouldExecute(targethook) for related type. - .Select(pair => pair.Key); - foreach (string target in noHookImplementation) - { - meta.Remove(target); + var relationshipValue = attr.GetValue(currentLayerEntity); + if (relationshipValue is IEnumerable relationshipCollection) + { + relationshipValue = relationshipCollection.Intersect(parsedEntities); + attr.SetValue(currentLayerEntity, relationshipValue); + } + else if (relationshipValue is IIdentifiable relationshipSingle) + { + if (!parsedEntities.Contains(relationshipValue)) + { + attr.SetValue(currentLayerEntity, null); + } + } } } - - IResourceHookContainer GetResourceDefinition(Type targetEntity) - { - return (IResourceHookContainer)_genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); - } - - //virtual public IList ExecuteHook(IList entities, string rel) - //{ - // // seeing as the relationships are already processed, we can just do - // // Logic.{method}(articles.Tags) - - // var entity = _jsonApiContext.RequestEntity; - - // var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == rel); - // Type l1 = typeof(List<>); - // if (relationship.GetType() == typeof(HasManyThroughAttribute)) - // { - // var castRelationship = relationship as HasManyThroughAttribute; - // object nestedLogic; - // for (int index = 0; index < entities.Count(); ++index) - // { - // // this is our {Article} - // var listEntity = entities[index]; - // // Get the {Article.ArticleTags} - // var relevantProperty = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); - // var intermediateEntities = relevantProperty.GetValue(listEntity) as IList; - - // // Logic for this nested property - // nestedLogic = GetLogic(castRelationship.Type); - - // // We default to OnList at the moment - // var method = nestedLogic.GetType().GetMethods().First(e => e.Name == "OnList"); - - // // get Tags, this can be replaced with some SelectMany's - // Type constructed = l1.MakeGenericType(castRelationship.RightProperty.PropertyType); - - // // Not happy with this, but was needed. - // IList toHold = Activator.CreateInstance(constructed) as IList; - // foreach (var iEntity in intermediateEntities) - // { - // // Iterating over the {ArticleTag}s to get all the {Tag}s - // var fetchedTags = (iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance).GetValue(iEntity)); - // toHold.Add(fetchedTags); - // } - // // - - // IEnumerable filteredTags = method.Invoke(nestedLogic, new object[] { toHold }) as IEnumerable; - // toHold = filteredTags as IList; - - // var toHoldHashSet = (HashSet)GetHashSet((IEnumerable)filteredTags); - // // We no process all {Article.ArticleTags} in a for loop because we're changing it - // for (int i = 0; i < intermediateEntities.Count; i++) - // { - // var iEntity = intermediateEntities[i]; - // var property = iEntity.GetType().GetProperty(castRelationship.RightProperty.Name, BindingFlags.Public | BindingFlags.Instance); - - // var item = property.GetValue(iEntity); - - // if (!toHoldHashSet.ToList().Contains(item)) - // { - // // What we first did: - // //property.SetValue(iEntity, null); - // // if {tag} is not filled, we shouldnt fill in {ArticleTag} because otherwise - // // JsonApiDotNetCore will try to show a null value, resulting in object refrence not set to an instance of an object - // // so we delete it here. - // // We shouldnt remove, I'm just emulating the WHERE here... - // intermediateEntities.RemoveAt(i); - // } - // else - // { - // // dont need to do anything - // // Maybe, in the future, when patches are done we need to do - // // this: - // // intermediateEntities[i] = iEntity; - // } - // } - // PropertyInfo prop = listEntity.GetType().GetProperty(castRelationship.InternalThroughName, BindingFlags.Public | BindingFlags.Instance); - // if (null != prop && prop.CanWrite) - // { - // prop.SetValue(listEntity, intermediateEntities); - // } - - - // entities[index] = listEntity; - - // } - - - // } - // return entities; - - //} - //private HashSet GetHashSet(IEnumerable source) - //{ - // return new HashSet(source); - //} - //private static List ConvertList(List value, Type type) - //{ - // return new List(value.Select(item => (T)Convert.ChangeType(item, type))); - //} - - - //private IQueryable CallLogic(IQueryable entities, IResourceDefinition resourceDefinition) where TType : class, IIdentifiable - //{ - // Type resourceType = resourceDefinition.GetType(); - // MethodInfo getMethod = resourceType.GetMethod("OnList", BindingFlags.Public); - // MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { typeof(TType) }); - - - // return (IQueryable)genericGet.Invoke(resourceType, new object[] { entities }); - //} - - - - - - } } diff --git a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs new file mode 100644 index 0000000000..8b1b781fc6 --- /dev/null +++ b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Services +{ + public interface IResourceHookMetaInfo + { + IEnumerable GetMetaEntries(IIdentifiable currentLayerEntity); + IResourceHookContainer GetResourceDefinition(Type targetEntity, ResourceHook hook = ResourceHook.None); + IResourceHookContainer GetResourceDefinition(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable; + Dictionary UpdateMetaInformation(IEnumerable nextLayerTypes, ResourceHook hook = ResourceHook.None); + } + + public class ResourceHookMetaInfo : IResourceHookMetaInfo + { + + protected readonly IGenericProcessorFactory _genericProcessorFactory; + protected readonly IResourceGraph _graph; + protected readonly Dictionary> _executors; + protected ResourceHook _hookInTreeTraversal; + protected Dictionary _meta; + + public ResourceHookMetaInfo( + IGenericProcessorFactory genericProcessorFactory, + IResourceGraph graph + ) + { + _genericProcessorFactory = genericProcessorFactory; + _graph = graph; + _meta = new Dictionary(); + _executors = new Dictionary>(); + } + + + public IEnumerable GetMetaEntries(IIdentifiable currentLayerEntity) + { + foreach (string metaKey in _meta.Keys) + { + var attribute = _meta[metaKey]; + + /// because currentLayer is not type-homogeneous (which is + /// why we need to use IIdentifiable for the list type of + /// that layer), we need to check if relatedType is really + /// related to parentType. We do this through comparison of Metakey + string requiredMetaKey = CreateMetaKey(attribute, currentLayerEntity.GetType()); + if (metaKey != requiredMetaKey) continue; + yield return attribute; + } + } + + /// + /// For a particular ResourceHook, checks if the ResourceDefinition has it implemented + /// and if so, return it. + /// + /// The resource definition. + /// Target entity type + public IResourceHookContainer GetResourceDefinition(Type targetEntity, ResourceHook hook = ResourceHook.None) + { + hook = (hook == ResourceHook.None) ? _hookInTreeTraversal : hook; + if (!_executors.TryGetValue(targetEntity, out IResourceHookContainer executor)) + { + executor = (IResourceHookContainer)_genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); + } + _executors[targetEntity] = executor; + if (!executor.ShouldExecuteHook(hook)) executor = null; + return executor; + } + + /// + /// For a particular ResourceHook, checks if the ResourceDefinition has it implemented + /// and if so, return it. + /// + /// The resource definition. + /// Target entity type + public IResourceHookContainer GetResourceDefinition(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable + { + return (IResourceHookContainer)GetResourceDefinition(typeof(TEntity), hook); + } + + /// + /// Creates a (helper) dictionary containing meta information needed for + /// the traversal of the next layer. It contains as + /// keys: Type, namely typeof(TRelatedType) that will occur in the traversal + /// of the next layer, + /// values: a Tuple of + /// * RelationshipAttribute (that contains getters and setters) + /// * IResourceHookExecutor{TRelatedType} to access the actual (nested) hook + /// + /// The meta dict. + /// Unique list of types to extract metadata from + /// The target resource hook type + public Dictionary + UpdateMetaInformation( + IEnumerable nextLayerTypes, + ResourceHook hook = ResourceHook.None) + { + + _hookInTreeTraversal = _hookInTreeTraversal != + ResourceHook.None ? + _hookInTreeTraversal : + hook; + foreach (Type targetType in nextLayerTypes) + { + var contextEntity = _graph.GetContextEntity(targetType); + var relationshipsForContextEntity = contextEntity.Relationships.ToDictionary( + attr => CreateMetaKey(attr, targetType, checkForDuplicates: true), + attr => attr); + /// keep only the meta info we really need for the traversal of the next layer + /// also remove duplicates. + PruneMetaDictionary(relationshipsForContextEntity, _hookInTreeTraversal); + _meta = _meta.Concat(relationshipsForContextEntity) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } + return _meta; + } + + + + /// + /// Creates the key for the meta dict. The RelationshipAttribute that is + /// in the value of the meta dict is specific for a particular related type + /// AS WELL AS parent type. This is reflected by the format of the meta key. + /// + /// The meta key. + /// Relationship attribute + /// Parent type. + string CreateMetaKey(RelationshipAttribute attr, Type parentType, bool checkForDuplicates = false) + { + var relationType = attr.IsHasOne ? "has-one" : "has-many"; + string newKey = $"{parentType.Name} {relationType} {attr.Type.Name}"; + if (checkForDuplicates && _meta.ContainsKey(newKey)) + { + return $"DUPLICATE-{Guid.NewGuid()}"; + } + return newKey; + } + + /// + /// Gets rid of keys in the meta dict that won't be needed for the next layer. + /// + /// It does so by: + /// 1) checking if there was at all a IResourceHookExecutor + /// implemented for this type (ResourceDefinition by default); + /// 2) then checking if there is a implementation of the particular + /// target hook. + /// + void PruneMetaDictionary( + Dictionary meta, + ResourceHook targetHook) + { + var dupes = meta.Where(pair => pair.Key.Contains("DUPLICATE")).Select(pair => pair.Key); + foreach (string target in dupes) + { + meta.Remove(target); + } + var noHookImplementation = meta.Where(pair => GetResourceDefinition(pair.Value.Type, targetHook) == null).Select(pair => pair.Key); + foreach (string target in noHookImplementation) + { + meta.Remove(target); + } + } + + + } +} \ No newline at end of file From 945abb42f4f1890474600422ea14fb1a0756a7e1 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Fri, 12 Apr 2019 15:07:10 +0200 Subject: [PATCH 020/168] chore: prior tests are passing again and updated implementation in service layer --- .../IServiceCollectionExtensions.cs | 1 + .../Internal/Generics/GenericProcessor.cs | 13 +-- .../Internal/ImplementedResourceHooks.cs | 10 +- .../Models/ResourceDefinition.cs | 31 ++++-- .../Services/EntityResourceService.cs | 38 ++++--- .../Services/ResourceHookExecutor.cs | 20 +++- test/UnitTests/Models/ResourceHookTests.cs | 101 +++++------------- 7 files changed, 101 insertions(+), 113 deletions(-) diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index 0d84442add..e620cbe87d 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -157,6 +157,7 @@ public static void AddJsonApiInternals( services.AddScoped(typeof(IResourceHookExecutor<>), typeof(ResourceHookExecutor<>)); services.AddSingleton(typeof(IImplementedResourceHooks<>), typeof(ImplementedResourceHooks<>)); + services.AddSingleton(); } private static void AddOperationServices(IServiceCollection services) diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs index a55c7be3b9..f786bb8b1b 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs @@ -6,6 +6,7 @@ using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; @@ -95,21 +96,21 @@ private async Task SetRelationshipsAsync(object parent, RelationshipAttribute re // TODO: need to handle the failure mode when the relationship does not implement IIdentifiable var entities = _context.Set().Where(x => relationshipIds.Contains(((IIdentifiable)x).StringId)).ToList(); - // @TODO implement hook executor - // entities.Select( e => _hookExecutor.BeforeUpdate(e, ResourceAction.UpdateRelationships)) + // @TODO implement hook executor @TODO Do we need this here or is sufficient in service? + // entities = _hookExecutor.BeforeUpdate(entities, ResourceAction.PatchRelationships) relationship.SetValue(parent, entities); // @TODO implement hook executor - // entities.ForEach( e => _hookExecutor.AfterUpdate(e, ResourceAction.UpdateRelationships)) + // entities = _hookExecutor.AfterUpdate(entities, ResourceAction.PatchRelationships) } else { // TODO: need to handle the failure mode when the relationship does not implement IIdentifiable var entity = _context.Set().SingleOrDefault(x => relationshipIds.First() == ((IIdentifiable)x).StringId); - // @TODO implement hook executor - // entity = _hookExecutor.BeforeUpdate(entity, ResourceAction.UpdateRelationships) + // @TODO implement hook executor @TODO Do we need this here or is sufficient in service? + // entity = _hookExecutor.BeforeUpdate(AsList(entities), ResourceAction.PatchRelationships).SingleOrDefault() relationship.SetValue(parent, entity); // @TODO implement hook executor - // _hookExecutor.AfterUpdate(entity, ResourceAction.UpdateRelationships) + } await _context.SaveChangesAsync(); diff --git a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs index bd2d864d96..9980fd3418 100644 --- a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs +++ b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs @@ -22,8 +22,8 @@ public enum ResourceHook /// /// A singleton service for a particular TEntity that stores a field of - /// enums that represents which hooks have been implemented for a particular - /// entity. + /// enums that represents which resource hooks have been implemented for that + /// particular entity. /// public interface IImplementedResourceHooks where TEntity : class, IIdentifiable { @@ -35,12 +35,16 @@ public interface IImplementedResourceHooks where TEntity : class, IIden /// public class ImplementedResourceHooks : IImplementedResourceHooks where TEntity : class, IIdentifiable { - private readonly ResourceHook[] _allHooks = Enum.GetValues(typeof(ResourceHook)) as ResourceHook[]; + private readonly ResourceHook[] _allHooks; private bool _isInitialized; public ResourceHook[] ImplementedHooks { get; private set; } public ImplementedResourceHooks() { + _allHooks = Enum.GetValues(typeof(ResourceHook)) + .Cast() + .Where(h => h != ResourceHook.None) + .ToArray(); DiscoverImplementedHooksForModel(); } diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 12aba635c5..ad4f128859 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -31,12 +31,14 @@ public class ResourceDefinition : IResourceDefinition, IResourceHookContainer private bool _requestCachedAttrsHaveBeenLoaded = false; private List _requestCachedAttrs; + protected readonly ResourceHook[] _implementedHooks; - public ResourceDefinition() + public ResourceDefinition(IImplementedResourceHooks implementedResourceHooks = null) { _graph = ResourceGraph.Instance; _contextEntity = ResourceGraph.Instance.GetContextEntity(typeof(T)); _instanceAttrsAreSpecified = InstanceOutputAttrsAreSpecified(); + _implementedHooks = implementedResourceHooks?.ImplementedHooks; } private bool InstanceOutputAttrsAreSpecified() @@ -166,16 +168,23 @@ private List GetOutputAttrs() /// public virtual QueryFilters GetQueryFilters() => null; + /// - public virtual T BeforeCreate(T entity, ResourceAction actionSource) + public virtual bool ShouldExecuteHook(ResourceHook hook) { - return entity; + return _implementedHooks.Contains(hook); } /// - public virtual T AfterCreate(T entity, ResourceAction actionSource) + public virtual IEnumerable BeforeCreate(IEnumerable entities, ResourceAction actionSource) { - return entity; + return entities; + } + + /// + public virtual IEnumerable AfterCreate(IEnumerable entities, ResourceAction actionSource) + { + return entities; } /// @@ -191,25 +200,25 @@ public virtual IEnumerable AfterRead(IEnumerable entities, ResourceAction } /// - public virtual T BeforeUpdate(T entity, ResourceAction actionSource) + public virtual IEnumerable BeforeUpdate(IEnumerable entities, ResourceAction actionSource) { - return entity; + return entities; } /// - public virtual T AfterUpdate(T entity, ResourceAction actionSource) + public virtual IEnumerable AfterUpdate(IEnumerable entities, ResourceAction actionSource) { - return entity; + return entities; } /// - public virtual void BeforeDelete(T entity, ResourceAction actionSource) + public virtual void BeforeDelete(IEnumerable entities, ResourceAction actionSource) { return; } /// - public virtual void AfterDelete(T entity, bool succeeded, ResourceAction actionSource) + public virtual void AfterDelete(IEnumerable entities, bool succeeded, ResourceAction actionSource) { return; } diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index cd3c18d21c..679d6b1446 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -80,7 +80,7 @@ public virtual async Task CreateAsync(TResource resource) { var entity = MapIn(resource); // @TODO implement hook executor - // entity = _hookExecutor.BeforeCreate(entity, ResourceAction.Create) + // entity = _hookExecutor.BeforeCreate(AsList(entity), ResourceAction.Create).SingleOrDefault(); entity = await _entities.CreateAsync(entity); @@ -96,11 +96,10 @@ public virtual async Task CreateAsync(TResource resource) } // @TODO implement hook executor - // entity = _hookExecutor.AfterCreate(entity, ResourceAction.Create) + // entity = _hookExecutor.AfterCreate(AsList(entity), ResourceAction.Create).SingleOrDefault(); return MapOut(entity); } - public virtual async Task DeleteAsync(TId id) { // @TODO the implementation of the DeleteAsync on repo level needs @@ -110,17 +109,17 @@ public virtual async Task DeleteAsync(TId id) // var succeeded = await _entities.DeleteAsync(entity); // so that we can use the entity variable in our hook execution. // @TODO implement hook executor - // _hookExecutor.BeforeDelete(entity, ResourceAction.Delete) + // _hookExecutor.BeforeDelete(AsList(entity), ResourceAction.Delete); var succeeded = await _entities.DeleteAsync(id); // @TODO implement hook executor - // _hookExecutor.AfterDelete(entity, succeeded, ResourceAction.Delete) + // _hookExecutor.AfterDelete(AsList(entity), succeeded, ResourceAction.Delete); return succeeded; } public virtual async Task> GetAsync() { // @TODO implement hook executor - // _hookExecutor.BeforeRead(ResourceAction.Read) + // _hookExecutor.BeforeRead(ResourceAction.Get); var entities = _entities.Get(); entities = ApplySortAndFilterQuery(entities); @@ -129,7 +128,7 @@ public virtual async Task> GetAsync() entities = IncludeRelationships(entities, _jsonApiContext.QuerySet.IncludedRelationships); // @TODO implement hook executor - // _hookExecutor.AfterRead(entities, ResourceAction.Read) + // entities = _hookExecutor.AfterRead(entities, ResourceAction.Get); // note: The hookexecutor will also fire the BeforeRead and AfterRead hooks // for every included entity. @@ -144,18 +143,19 @@ public virtual async Task> GetAsync() public virtual async Task GetAsync(TId id) { // @TODO implement hook executor - // _hookExecutor.BeforeRead(ResourceAction.ReadSingle, id) + // _hookExecutor.BeforeRead(ResourceAction.GetSingle, id.ToString()); //TResource entity = null; TEntity entity; if (ShouldIncludeRelationships()) { entity = await GetWithRelationshipsAsync(id); - } else + } + else { entity = await _entities.GetAsync(id); } // @TODO implement hook executor - // entity = _hookExecutor.AfterRead(entity, ResourceAction.ReadSingle).SingleOrDefault(); + // entity = _hookExecutor.AfterRead(AsList(entity), ResourceAction.GetSingle).SingleOrDefault(); // note: The hookexecutor will also fire the BeforeRead and AfterRead hooks // for every included entity. return MapOut(entity); @@ -171,7 +171,7 @@ public virtual async Task GetRelationshipAsync(TId id, string relationsh // _hookExecutor.BeforeRead(id, ResourceAction.GetRelationship) var entity = await _entities.GetAndIncludeAsync(id, relationshipName); // @TODO implement hook executor - // entity = _hookExecutor.AfterRead(entity, ResourceAction.GetRelationship).SingleOrDefault(); + // entity = _hookExecutor.AfterRead(AsList(entity), ResourceAction.GetRelationship).SingleOrDefault(); // note: The hookexecutor will also fire the BeforeRead and AfterRead hooks // for every included entity. @@ -198,11 +198,10 @@ public virtual async Task UpdateAsync(TId id, TResource resource) var entity = MapIn(resource); // @TODO implement hook executor - // entity = _hookExecutor.BeforeUpdate(entity, ResourceAction.Update) + // entity = _hookExecutor.BeforeUpdate(AsList(entity), ResourceAction.Update).SingleOrDefault(); entity = await _entities.UpdateAsync(id, entity); // @TODO implement hook executor - // entity = _hookExecutor.AfterUpdate(entity, ResourceAction.Update) - + // entity = _hookExecutor.AfterUpdate(AsList(entity), ResourceAction.Update).SingleOrDefault(); return MapOut(entity); } @@ -238,14 +237,14 @@ public virtual async Task UpdateRelationshipsAsync(TId id, string relationshipNa // @TODO implement hook executor - // entity = _hookExecutor.BeforeUpdate(entity, ResourceAction.UpdateRelationships) + // entity = _hookExecutor.BeforeUpdate(AsList(entity), ResourceAction.UpdateRelationships).SingleOrDefault(); await _entities.UpdateRelationshipsAsync(entity, relationship, relationshipIds); // Note the call in previous line relies on Generic Processor to update relations. - // In this call, _hookExecutor will call the BeforeUpdate and AfterUpdate + // In this call, _hookExecutor will call the BeforeUpdate and AfterUpdate.SingleOrDefault(); // hooks for the target relation entities. See the SetRelationshipsAsync // method in GenericProcessor.cs // @TODO implement hook executor - // _hookExecutor.AfterUpdate(entity, ResourceAction.UpdateRelationships) + // entity = _hookExecutor.AfterUpdate(AsList(entity), ResourceAction.PatchRelationships).SingleOrDefault(); relationship.Type = relationshipType; } @@ -334,5 +333,10 @@ private TEntity MapIn(TResource resource) => (typeof(TResource) == typeof(TEntity)) ? resource as TEntity : _mapper.Map(resource); + + private TEntity[] AsList(TEntity entity) + { + return new TEntity[] { entity }; + } } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index 651a0d3d8b..5996e32d64 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -55,7 +55,7 @@ public virtual IEnumerable BeforeCreate(IEnumerable entities, _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.BeforeUpdate); BreadthFirstTraverse(entities, (container, relatedEntities) => { - return container.BeforeUpdate(relatedEntities, actionSource); + return container.BeforeUpdate(relatedEntities, actionSource); }); return entities; @@ -85,9 +85,15 @@ public virtual IEnumerable AfterRead(IEnumerable entities, Res { var hookContainer = _meta.GetResourceDefinition(ResourceHook.AfterRead); if (hookContainer == null) return entities; + entities = hookContainer.AfterRead(entities, actionSource); - - throw new NotImplementedException(); + _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.BeforeUpdate); + BreadthFirstTraverse(entities, (container, relatedEntities) => + { + container.BeforeRead(actionSource); + return container.AfterRead(relatedEntities, actionSource); + }); + return entities; } /// @@ -128,6 +134,11 @@ public virtual void AfterDelete(IEnumerable entities, bool succeeded, R hookContainer.AfterDelete(entities, succeeded, actionSource); } + /// + /// Fires the hooks for related (nested) entities. + /// + /// Current layer. + /// Hook execution action. void BreadthFirstTraverse( IEnumerable currentLayer, Func, IEnumerable, IEnumerable> hookExecutionAction @@ -242,7 +253,8 @@ Dictionary> relationshipsInCur { if (!parsedEntities.Contains(relationshipValue)) { - attr.SetValue(currentLayerEntity, null); + attr.SetValue(currentLayerEntity, null); + } } } } diff --git a/test/UnitTests/Models/ResourceHookTests.cs b/test/UnitTests/Models/ResourceHookTests.cs index c8db60f1d9..ba51454480 100644 --- a/test/UnitTests/Models/ResourceHookTests.cs +++ b/test/UnitTests/Models/ResourceHookTests.cs @@ -8,8 +8,7 @@ using System; using System.Collections.Generic; using Xunit; -using Dummy = UnitTests.Models.AllHooksWithRelations.Dummy; -using DummyResourceDefinition = UnitTests.Models.AllHooksWithRelations.DummyResourceDefinition; + namespace UnitTests.Models { @@ -28,7 +27,7 @@ public ResourceHooks_Tests() public void Hook_Discovery() { // arrange & act - var hookConfig = new ImplementedResourceHooks(); + var hookConfig = new ImplementedResourceHooks(); // assert Assert.Contains(ResourceHook.BeforeDelete, hookConfig.ImplementedHooks); Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); @@ -41,9 +40,9 @@ public void BeforeCreate_Hook_Is_Called() // arrange (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); // act - hookExecutor.BeforeCreate(It.IsAny(), It.IsAny()); + hookExecutor.BeforeCreate(It.IsAny>(), It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.BeforeCreate(It.IsAny(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] @@ -52,9 +51,9 @@ public void AfterCreate_Hook_Is_Called() // arrange (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); // act - hookExecutor.AfterCreate(It.IsAny(), It.IsAny()); + hookExecutor.AfterCreate(It.IsAny>(), It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.AfterCreate(It.IsAny(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.AfterCreate(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void BeforeRead_Hook_Is_Called() @@ -82,9 +81,9 @@ public void BeforeUpdate_Hook_Is_Called() // arrange (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); // act - hookExecutor.BeforeUpdate(It.IsAny(), It.IsAny()); + hookExecutor.BeforeUpdate(It.IsAny>(), It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.BeforeUpdate(It.IsAny(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void AfterUpdate_Hook_Is_Called() @@ -92,9 +91,9 @@ public void AfterUpdate_Hook_Is_Called() // arrange (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); // act - hookExecutor.AfterUpdate(It.IsAny(), It.IsAny()); + hookExecutor.AfterUpdate(It.IsAny>(), It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.AfterUpdate(It.IsAny(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void BeforeDelete_Hook_Is_Called() @@ -103,10 +102,10 @@ public void BeforeDelete_Hook_Is_Called() (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); // act - hookExecutor.BeforeDelete(It.IsAny(), It.IsAny()); + hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void AfterDelete_Hook_Is_Called() @@ -114,21 +113,21 @@ public void AfterDelete_Hook_Is_Called() // arrange (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); // act - hookExecutor.AfterDelete(It.IsAny(), true, It.IsAny()); + hookExecutor.AfterDelete(It.IsAny>(), true, It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny(), true, It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), true, It.IsAny()), Times.Once()); } Mock CreateResourceDefinitionMock() { var resourceDefinition = new Mock(); - resourceDefinition.Setup(rd => rd.BeforeCreate(It.IsAny(), It.IsAny())); - resourceDefinition.Setup(rd => rd.AfterCreate(It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterCreate(It.IsAny>(), It.IsAny())); resourceDefinition.Setup(rd => rd.BeforeRead(It.IsAny(), null)); resourceDefinition.Setup(rd => rd.AfterRead(It.IsAny>(), It.IsAny())); - resourceDefinition.Setup(rd => rd.BeforeUpdate(It.IsAny(), It.IsAny())); - resourceDefinition.Setup(rd => rd.AfterUpdate(It.IsAny(), It.IsAny())); - resourceDefinition.Setup(rd => rd.BeforeDelete(It.IsAny(), It.IsAny())); - resourceDefinition.Setup(rd => rd.AfterDelete(It.IsAny(), It.IsAny(), It.IsAny())); + resourceDefinition.Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny())); + resourceDefinition.Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())); + resourceDefinition.Setup(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny())); return resourceDefinition; } @@ -143,70 +142,28 @@ Mock CreateResourceDefinitionMock() var context = new Mock(); context.Setup(c => c.GenericProcessorFactory).Returns(processorFactory.Object); - var hookExecutor = new ResourceHookExecutor(context.Object, new ImplementedResourceHooks()); + var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); + var hookExecutor = new ResourceHookExecutor(context.Object, new ImplementedResourceHooks(), meta); return (resourceDefinition, context, hookExecutor); } } - public class PartialHooks - { - public class Dummy : Identifiable - { - } - public class DummyResourceDefinition : ResourceDefinition - { - public override void BeforeDelete(Dummy entity, ResourceAction actionSource) - { - } - public override void AfterDelete(Dummy entity, bool succeeded, ResourceAction actionSource) - { + public class Dummy : Identifiable + { - } - } } - public class AllHooksWithRelations + public class DummyResourceDefinition : ResourceDefinition { - public class Dummy : Identifiable + public override void BeforeDelete(IEnumerable entities, ResourceAction actionSource) { - } - - public class DummyResourceDefinition : ResourceDefinition + public override void AfterDelete(IEnumerable entities, bool succeeded, ResourceAction actionSource) { - public override Dummy BeforeCreate(Dummy entity, ResourceAction actionSource) - { - return entity; - } - public override Dummy AfterCreate(Dummy entity, ResourceAction actionSource) - { - return entity; - } - public override void BeforeRead(ResourceAction actionSource, string stringId = null) - { - - } - public override IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) - { - return entities; - } - public override Dummy BeforeUpdate(Dummy entity, ResourceAction actionSource) - { - return entity; - } - public override Dummy AfterUpdate(Dummy entity, ResourceAction actionSource) - { - return entity; - } - public override void BeforeDelete(Dummy entity, ResourceAction actionSource) - { - } - public override void AfterDelete(Dummy entity, bool succeeded, ResourceAction actionSource) - { - - } + } } + } From 87289e231017601b1d1fe224f56facc60f94ff03 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 16 Apr 2019 14:18:14 +0200 Subject: [PATCH 021/168] test: updated unit tests for beforecreate and aftercreate --- .../Resources/PersonResource.cs | 14 ++ .../Resources/TodoResource.cs | 17 ++ .../IServiceCollectionExtensions.cs | 1 + .../Services/IResourceHookExecutor.cs | 7 +- .../Services/ResourceHookExecutor.cs | 54 +++-- .../Services/ResourceHookMetaInfo.cs | 47 ++-- test/UnitTests/Models/ResourceHookTests.cs | 201 ++++++++++++++---- 7 files changed, 262 insertions(+), 79 deletions(-) create mode 100644 src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs create mode 100644 src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs new file mode 100644 index 0000000000..477c548f72 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Linq; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; + +namespace JsonApiDotNetCoreExample.Resources +{ + public class PersonResource : ResourceDefinition + { + public override IEnumerable BeforeUpdate(IEnumerable entities, ResourceAction actionSource) { return entities; } + public override IEnumerable AfterUpdate(IEnumerable entities, ResourceAction actionSource) { return entities; } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs new file mode 100644 index 0000000000..8351f2bbe1 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Linq; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; + +namespace JsonApiDotNetCoreExample.Resources +{ + public class TodoItemResource : ResourceDefinition + { + public override IEnumerable BeforeCreate(IEnumerable entities, ResourceAction actionSource) { return entities; } + public override IEnumerable AfterCreate(IEnumerable entities, ResourceAction actionSource) { return entities; } + + public override void BeforeDelete(IEnumerable entities, ResourceAction actionSource) { } + public override void AfterDelete(IEnumerable entities, bool succeeded, ResourceAction actionSource) { } + } +} diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index e620cbe87d..6ad349fc1f 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -156,6 +156,7 @@ public static void AddJsonApiInternals( services.AddScoped(); services.AddScoped(typeof(IResourceHookExecutor<>), typeof(ResourceHookExecutor<>)); + services.AddScoped(typeof(IResourceHookContainer<>), typeof(ResourceDefinition<>)); services.AddSingleton(typeof(IImplementedResourceHooks<>), typeof(ImplementedResourceHooks<>)); services.AddSingleton(); } diff --git a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs index 9fd1bd9d03..3916d90f40 100644 --- a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs @@ -132,7 +132,7 @@ public interface IResourceHookBase where T : class, IIdentifiable /// See IResourceHookExecutor for details on the /// resource hook execution flow. /// - public interface IResourceHookContainer : IResourceHookBase where T : class, IIdentifiable + public interface IResourceHookContainer : IResourceHookContainer, IResourceHookBase where T : class, IIdentifiable { /// /// Checks whether a hook should be executed or not through reflective @@ -144,6 +144,11 @@ public interface IResourceHookContainer : IResourceHookBase where T : clas } + public interface IResourceHookContainer + { + + } + /// /// A utility class responsible for executing hooks as defined in diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index 5996e32d64..935e9caad7 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -40,7 +40,7 @@ IResourceHookMetaInfo meta public virtual IEnumerable BeforeCreate(IEnumerable entities, ResourceAction actionSource) { - var hookContainer = _meta.GetResourceDefinition(ResourceHook.BeforeCreate); + var hookContainer = _meta.GetResourceHookContainer(ResourceHook.BeforeCreate); if (hookContainer == null) return entities; /// traversing the 0th layer. Not including this in the recursive function /// because the most complexities that arrise in the tree traversal do not @@ -64,16 +64,26 @@ public virtual IEnumerable BeforeCreate(IEnumerable entities, /// public virtual IEnumerable AfterCreate(IEnumerable entities, ResourceAction actionSource) { - var hookContainer = _meta.GetResourceDefinition(ResourceHook.AfterCreate); - if (hookContainer == null) return entities; + var hookContainer = _meta.GetResourceHookContainer(ResourceHook.AfterCreate); + /// @TODO: even if we don't have an implementation for eg TodoItem AfterCreate, + /// we should still consider to fire the hooks of its relation, eg TodoItem.Owner + if (hookContainer == null) return entities; + + entities = hookContainer.AfterCreate(entities, actionSource); + + _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.AfterUpdate); + BreadthFirstTraverse(entities, (container, relatedEntities) => + { + return container.AfterUpdate(relatedEntities, actionSource); + }); - return hookContainer.AfterCreate(entities, actionSource); + return entities; } /// public virtual void BeforeRead(ResourceAction actionSource, string stringId = null) { - var hookContainer = _meta.GetResourceDefinition(ResourceHook.BeforeRead); + var hookContainer = _meta.GetResourceHookContainer(ResourceHook.BeforeRead); if (hookContainer == null) return; @@ -83,7 +93,7 @@ public virtual void BeforeRead(ResourceAction actionSource, string stringId = nu /// public virtual IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) { - var hookContainer = _meta.GetResourceDefinition(ResourceHook.AfterRead); + var hookContainer = _meta.GetResourceHookContainer(ResourceHook.AfterRead); if (hookContainer == null) return entities; entities = hookContainer.AfterRead(entities, actionSource); @@ -99,7 +109,7 @@ public virtual IEnumerable AfterRead(IEnumerable entities, Res /// public virtual IEnumerable BeforeUpdate(IEnumerable entities, ResourceAction actionSource) { - var hookContainer = _meta.GetResourceDefinition(ResourceHook.BeforeUpdate); + var hookContainer = _meta.GetResourceHookContainer(ResourceHook.BeforeUpdate); if (hookContainer == null) return entities; @@ -109,7 +119,7 @@ public virtual IEnumerable BeforeUpdate(IEnumerable entities, /// public virtual IEnumerable AfterUpdate(IEnumerable entities, ResourceAction actionSource) { - var hookContainer = _meta.GetResourceDefinition(ResourceHook.AfterUpdate); + var hookContainer = _meta.GetResourceHookContainer(ResourceHook.AfterUpdate); if (hookContainer == null) return entities; @@ -119,7 +129,7 @@ public virtual IEnumerable AfterUpdate(IEnumerable entities, R /// public virtual void BeforeDelete(IEnumerable entities, ResourceAction actionSource) { - var hookContainer = _meta.GetResourceDefinition(ResourceHook.BeforeDelete); + var hookContainer = _meta.GetResourceHookContainer(ResourceHook.BeforeDelete); if (hookContainer == null) return; hookContainer.BeforeDelete(entities, actionSource); @@ -128,7 +138,7 @@ public virtual void BeforeDelete(IEnumerable entities, ResourceAction a /// public virtual void AfterDelete(IEnumerable entities, bool succeeded, ResourceAction actionSource) { - var hookContainer = _meta.GetResourceDefinition(ResourceHook.AfterDelete); + var hookContainer = _meta.GetResourceHookContainer(ResourceHook.AfterDelete); if (hookContainer == null) return; hookContainer.AfterDelete(entities, succeeded, actionSource); @@ -178,6 +188,8 @@ IEnumerable currentLayer foreach (RelationshipAttribute attr in _meta.GetMetaEntries(currentLayerEntity)) { var relationshipValue = attr.GetValue(currentLayerEntity); + // skip iteration if there is no relation assigned @TODO what about to-many: will we have empty lists or null? and how does this relate to that being Included by query params + if (relationshipValue == null) continue; if (!(relationshipValue is IEnumerable)) { // in the case of a to-one relationship, the assigned value @@ -211,18 +223,20 @@ void ExecutionLoop( Func, IEnumerable, IEnumerable> hookExecution ) { - foreach (var pair in relationshipsInCurrentLayer) + var relationships = relationshipsInCurrentLayer.Keys.ToArray(); + + foreach (var attr in relationships) { - var attr = pair.Key; - var uniqueEntities = new HashSet(pair.Value); - var executor = _meta.GetResourceDefinition(attr.Type); - var filteredUniqueEntites = hookExecution(executor, uniqueEntities); - relationshipsInCurrentLayer[pair.Key] = filteredUniqueEntites.ToArray(); + var entities = relationshipsInCurrentLayer[attr]; + var uniqueEntities = new HashSet(entities); + var innerHookContainer = _meta.GetResourceHookContainer(attr.Type); + var filteredUniqueEntites = hookExecution(innerHookContainer, uniqueEntities); + relationshipsInCurrentLayer[attr] = filteredUniqueEntites.ToArray(); } } /// - /// When this method is called, the values in relationshipsInCurrentLayer + /// B this method is called, the values in relationshipsInCurrentLayer /// will contain a subset compared to in the DoExtractionLoop call. /// We now need to iterate through currentLayer again and remove any of /// their related entities that do not occur in relationshipsInCurrentLayer @@ -241,7 +255,11 @@ Dictionary> relationshipsInCur { foreach (RelationshipAttribute attr in _meta.GetMetaEntries(currentLayerEntity)) { - var parsedEntities = relationshipsInCurrentLayer[attr]; + // skip the iteration there is nothing to a given relationship + if (!relationshipsInCurrentLayer.TryGetValue(attr, out var parsedEntities)) + { + continue; + } var relationshipValue = attr.GetValue(currentLayerEntity); if (relationshipValue is IEnumerable relationshipCollection) diff --git a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs index 8b1b781fc6..ec13d7407b 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs @@ -10,8 +10,8 @@ namespace JsonApiDotNetCore.Services public interface IResourceHookMetaInfo { IEnumerable GetMetaEntries(IIdentifiable currentLayerEntity); - IResourceHookContainer GetResourceDefinition(Type targetEntity, ResourceHook hook = ResourceHook.None); - IResourceHookContainer GetResourceDefinition(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable; + IResourceHookContainer GetResourceHookContainer(Type targetEntity, ResourceHook hook = ResourceHook.None); + IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable; Dictionary UpdateMetaInformation(IEnumerable nextLayerTypes, ResourceHook hook = ResourceHook.None); } @@ -20,7 +20,7 @@ public class ResourceHookMetaInfo : IResourceHookMetaInfo protected readonly IGenericProcessorFactory _genericProcessorFactory; protected readonly IResourceGraph _graph; - protected readonly Dictionary> _executors; + protected readonly Dictionary> _hookContainers; protected ResourceHook _hookInTreeTraversal; protected Dictionary _meta; @@ -32,7 +32,7 @@ IResourceGraph graph _genericProcessorFactory = genericProcessorFactory; _graph = graph; _meta = new Dictionary(); - _executors = new Dictionary>(); + _hookContainers = new Dictionary>(); } @@ -53,32 +53,43 @@ public IEnumerable GetMetaEntries(IIdentifiable currentLa } /// - /// For a particular ResourceHook, checks if the ResourceDefinition has it implemented + /// For a particular ResourceHook and for a given model type, checks if + /// the ResourceDefinition has an implementation for the hook /// and if so, return it. + /// + /// Also caches the retrieves containers so we don't need to reflectively + /// instantiate multiple times. /// /// The resource definition. - /// Target entity type - public IResourceHookContainer GetResourceDefinition(Type targetEntity, ResourceHook hook = ResourceHook.None) + /// Target entity type + /// The hook to get a ResourceDefinition for. + public IResourceHookContainer GetResourceHookContainer(Type targetEntity, ResourceHook hook = ResourceHook.None) { hook = (hook == ResourceHook.None) ? _hookInTreeTraversal : hook; - if (!_executors.TryGetValue(targetEntity, out IResourceHookContainer executor)) + if (!_hookContainers.TryGetValue(targetEntity, out IResourceHookContainer container)) { - executor = (IResourceHookContainer)_genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), targetEntity); + container = (IResourceHookContainer)_genericProcessorFactory.GetProcessor(typeof(IResourceHookContainer<>), targetEntity); + _hookContainers[targetEntity] = container; } - _executors[targetEntity] = executor; - if (!executor.ShouldExecuteHook(hook)) executor = null; - return executor; + if (container == null) return container; + if (!container.ShouldExecuteHook(hook)) container = null; + return container; } /// - /// For a particular ResourceHook, checks if the ResourceDefinition has it implemented + /// For a particular ResourceHook and for a given model type, checks if + /// the ResourceDefinition has an implementation for the hook /// and if so, return it. + /// + /// Also caches the retrieves containers so we don't need to reflectively + /// instantiate multiple times. /// /// The resource definition. /// Target entity type - public IResourceHookContainer GetResourceDefinition(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable + /// The hook to get a ResourceDefinition for. + public IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable { - return (IResourceHookContainer)GetResourceDefinition(typeof(TEntity), hook); + return (IResourceHookContainer)GetResourceHookContainer(typeof(TEntity), hook); } /// @@ -131,7 +142,7 @@ public Dictionary string CreateMetaKey(RelationshipAttribute attr, Type parentType, bool checkForDuplicates = false) { var relationType = attr.IsHasOne ? "has-one" : "has-many"; - string newKey = $"{parentType.Name} {relationType} {attr.Type.Name}"; + string newKey = $"{parentType.Name} {relationType} {attr.RelationshipPath}"; if (checkForDuplicates && _meta.ContainsKey(newKey)) { return $"DUPLICATE-{Guid.NewGuid()}"; @@ -152,12 +163,12 @@ void PruneMetaDictionary( Dictionary meta, ResourceHook targetHook) { - var dupes = meta.Where(pair => pair.Key.Contains("DUPLICATE")).Select(pair => pair.Key); + var dupes = meta.Where(pair => pair.Key.Contains("DUPLICATE")).Select(pair => pair.Key).ToArray(); foreach (string target in dupes) { meta.Remove(target); } - var noHookImplementation = meta.Where(pair => GetResourceDefinition(pair.Value.Type, targetHook) == null).Select(pair => pair.Key); + var noHookImplementation = meta.Where(pair => GetResourceHookContainer(pair.Value.Type, targetHook) == null).Select(pair => pair.Key).ToArray(); foreach (string target in noHookImplementation) { meta.Remove(target); diff --git a/test/UnitTests/Models/ResourceHookTests.cs b/test/UnitTests/Models/ResourceHookTests.cs index ba51454480..8bb6a70c00 100644 --- a/test/UnitTests/Models/ResourceHookTests.cs +++ b/test/UnitTests/Models/ResourceHookTests.cs @@ -4,9 +4,12 @@ using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; using Moq; using System; using System.Collections.Generic; +using System.Linq; using Xunit; @@ -20,7 +23,10 @@ public ResourceHooks_Tests() { // Build() exposes the static ResourceGraphBuilder.Instance member, which // is consumed by ResourceDefinition class. - new ResourceGraphBuilder().Build(); + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); } [Fact] @@ -38,28 +44,42 @@ public void Hook_Discovery() public void BeforeCreate_Hook_Is_Called() { // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; // act - hookExecutor.BeforeCreate(It.IsAny>(), It.IsAny()); + hookExecutor.BeforeCreate(todoInput, It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny()), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void AfterCreate_Hook_Is_Called() { // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; // act - hookExecutor.AfterCreate(It.IsAny>(), It.IsAny()); + hookExecutor.AfterCreate(todoInput, It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.AfterCreate(It.IsAny>(), It.IsAny()), Times.Once()); + todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void BeforeRead_Hook_Is_Called() { // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); // act hookExecutor.BeforeRead(It.IsAny()); // assert @@ -69,83 +89,180 @@ public void BeforeRead_Hook_Is_Called() public void AfterRead_Hook_Is_Called() { // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); // act - hookExecutor.AfterRead(It.IsAny>(), It.IsAny()); + hookExecutor.AfterRead(It.IsAny>(), It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void BeforeUpdate_Hook_Is_Called() { // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); // act - hookExecutor.BeforeUpdate(It.IsAny>(), It.IsAny()); + hookExecutor.BeforeUpdate(It.IsAny>(), It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void AfterUpdate_Hook_Is_Called() { // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); // act - hookExecutor.AfterUpdate(It.IsAny>(), It.IsAny()); + hookExecutor.AfterUpdate(It.IsAny>(), It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void BeforeDelete_Hook_Is_Called() { // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); // act - hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); + hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); } [Fact] public void AfterDelete_Hook_Is_Called() { // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(); + (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); // act - hookExecutor.AfterDelete(It.IsAny>(), true, It.IsAny()); + hookExecutor.AfterDelete(It.IsAny>(), true, It.IsAny()); // assert - resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), true, It.IsAny()), Times.Once()); - } - Mock CreateResourceDefinitionMock() - { - var resourceDefinition = new Mock(); - resourceDefinition.Setup(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny())); - resourceDefinition.Setup(rd => rd.AfterCreate(It.IsAny>(), It.IsAny())); - resourceDefinition.Setup(rd => rd.BeforeRead(It.IsAny(), null)); - resourceDefinition.Setup(rd => rd.AfterRead(It.IsAny>(), It.IsAny())); - resourceDefinition.Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())); - resourceDefinition.Setup(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny())); - resourceDefinition.Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())); - resourceDefinition.Setup(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny())); + resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), true, It.IsAny()), Times.Once()); + } + + + (Mock>, Mock, IResourceHookExecutor, Mock>) CreateTestObjectsForSimpleCase() + { + + // creates the resource definition mock and corresponding ImplementedHooks discovery instance + (var hooksDiscovery, var todoItemResource, var identifiableTodoItemResource) = CreateResourceDefinition(); + + // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + (var context, var processorFactory) = CreateContextAndProcessorMocks(); + + // wiring up the mocked GenericProcessorFactory to return the correct resource definition + SetupProcessorFactoryForResourceDefinition(processorFactory, todoItemResource.Object); + var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); + var hookExecutor = new ResourceHookExecutor(context.Object, hooksDiscovery, meta); + return (todoItemResource, context, hookExecutor, identifiableTodoItemResource); + } + + (Mock context, IResourceHookExecutor, Mock>, Mock>) + CreateTestObjectsForNestedCase() + where TMain : class, IIdentifiable + where TNested : class, IIdentifiable + { + + // creates the resource definition mock and corresponding ImplementedHooks discovery instance + (var mainDiscovery, var mainResource, var identifiableMainResource) = CreateResourceDefinition(); + var identifiableNestedResource = CreateResourceDefinition().Item3; + // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + (var context, var processorFactory) = CreateContextAndProcessorMocks(); + + SetupProcessorFactoryForResourceDefinition(processorFactory, mainResource.Object); + var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); + var hookExecutor = new ResourceHookExecutor(context.Object, mainDiscovery, meta); + + SetupProcessorFactoryForResourceDefinition(processorFactory, identifiableNestedResource.Object); + + return (context, hookExecutor, mainResource, identifiableNestedResource); + } + + Mock> ImplementAs( + Mock> resourceDefinition, + ImplementedResourceHooks discovery + ) where TImplementAs : class, IIdentifiable + where TActual : class, IIdentifiable + { + resourceDefinition + .As>() + .Setup(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.AfterCreate(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.BeforeRead(It.IsAny(), null)) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.AfterRead(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny())) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.ShouldExecuteHook(It.IsAny())) + .Returns((hook) => discovery.ImplementedHooks.Contains(hook)); + resourceDefinition + .As>() + .Setup(rd => rd.ShouldExecuteHook(It.IsAny())) + .Returns((hook) => discovery.ImplementedHooks.Contains(hook)); return resourceDefinition; } - (Mock, Mock, IResourceHookExecutor) CreateTestObjects() + (ImplementedResourceHooks, Mock>, Mock>) CreateResourceDefinition + () + where TModel : class, IIdentifiable { + var discovery = new ImplementedResourceHooks(); + var resourceDefinition = new Mock>(); + + var identifiableResourceDefinition = ImplementAs(resourceDefinition.As>(), discovery); + var modelSpecificResourceDefinition = ImplementAs(resourceDefinition.As>(), discovery); + + return (discovery, modelSpecificResourceDefinition, identifiableResourceDefinition); + } + - var resourceDefinition = CreateResourceDefinitionMock(); + (Mock, Mock) CreateContextAndProcessorMocks() + { var processorFactory = new Mock(); - processorFactory.Setup(c => c.GetProcessor(It.IsAny(), It.IsAny())) - .Returns((IResourceDefinition)resourceDefinition.Object); var context = new Mock(); context.Setup(c => c.GenericProcessorFactory).Returns(processorFactory.Object); + return (context, processorFactory); + } - var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); - var hookExecutor = new ResourceHookExecutor(context.Object, new ImplementedResourceHooks(), meta); - return (resourceDefinition, context, hookExecutor); + void SetupProcessorFactoryForResourceDefinition( + Mock processorFactory, + IResourceHookContainer modelResource) + where TModel : class, IIdentifiable + where TInnerContainerType : class, IIdentifiable + + { + processorFactory.Setup(c => c.GetProcessor(typeof(IResourceHookContainer<>), typeof(TModel))) + .Returns(modelResource); } } From 56d6708efd0a408baf113ac4b67330e21938ab9a Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 16 Apr 2019 15:07:43 +0200 Subject: [PATCH 022/168] test: made implementedhooks configurable --- .../Internal/ImplementedResourceHooks.cs | 37 ++++++++---------- test/UnitTests/Models/ResourceHookTests.cs | 38 +++++++++++++------ 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs index 9980fd3418..b83302277f 100644 --- a/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs +++ b/src/JsonApiDotNetCore/Internal/ImplementedResourceHooks.cs @@ -54,30 +54,23 @@ public ImplementedResourceHooks() /// The implemented hooks for model. void DiscoverImplementedHooksForModel() { - if (!_isInitialized) + var derivedTypes = TypeLocator.GetDerivedTypes(typeof(TEntity).Assembly, typeof(IResourceHookContainer)); + try { - _isInitialized = true; - } - else - { - throw new JsonApiSetupException($@"Implemented hooks may be discovered only once. - Adding such implementations at runtime is currently not supported."); - } - - Type resourceDefinitionImplementationType = null; - - foreach (var match in TypeLocator.GetDerivedTypes(typeof(TEntity).Assembly, typeof(ResourceDefinition))) - { - resourceDefinitionImplementationType = match; - break; - } - if (resourceDefinitionImplementationType != null) - { - ImplementedHooks = _allHooks.Where(h => resourceDefinitionImplementationType.GetMethod(h.ToString("G")).DeclaringType == resourceDefinitionImplementationType) - .ToArray(); - } else + Type targetType = derivedTypes.SingleOrDefault(); // multiple containers is not supported + if (targetType != null) + { + ImplementedHooks = _allHooks.Where(h => targetType.GetMethod(h.ToString("G")).DeclaringType == targetType) + .ToArray(); + } + else + { + ImplementedHooks = new ResourceHook[0]; + } + } catch (Exception e) { - ImplementedHooks = new ResourceHook[0]; + throw new JsonApiSetupException($@"Incorrect resource hook setup. For a given model of type TEntity, + only one class may implement IResourceHookContainer"); } } diff --git a/test/UnitTests/Models/ResourceHookTests.cs b/test/UnitTests/Models/ResourceHookTests.cs index 8bb6a70c00..d2e68d98b5 100644 --- a/test/UnitTests/Models/ResourceHookTests.cs +++ b/test/UnitTests/Models/ResourceHookTests.cs @@ -40,8 +40,16 @@ public void Hook_Discovery() Assert.Equal(2, hookConfig.ImplementedHooks.Length); } + IImplementedResourceHooks SetDiscoverableHooks(ResourceHook[] implementedHooks) + where TEntity : class, IIdentifiable + { + var mock = new Mock>(); + mock.Setup(discovery => discovery.ImplementedHooks) + .Returns(implementedHooks); + return mock.Object; + } [Fact] - public void BeforeCreate_Hook_Is_Called() + public void BeforeCreate_Hook_Is_Called_With_Nested_BeforeUpdate() { // arrange (var contextMock, var hookExecutor, var todoResourceMock, @@ -59,7 +67,7 @@ public void BeforeCreate_Hook_Is_Called() } [Fact] - public void AfterCreate_Hook_Is_Called() + public void AfterCreate_Hook_Is_Called_With_Nested_AfterUpdate() { // arrange (var contextMock, var hookExecutor, var todoResourceMock, @@ -142,8 +150,9 @@ public void AfterDelete_Hook_Is_Called() (Mock>, Mock, IResourceHookExecutor, Mock>) CreateTestObjectsForSimpleCase() { + var discovery = new ImplementedResourceHooks(); // creates the resource definition mock and corresponding ImplementedHooks discovery instance - (var hooksDiscovery, var todoItemResource, var identifiableTodoItemResource) = CreateResourceDefinition(); + (var todoItemResource, var identifiableTodoItemResource) = CreateResourceDefinition(discovery); // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. (var context, var processorFactory) = CreateContextAndProcessorMocks(); @@ -151,19 +160,25 @@ public void AfterDelete_Hook_Is_Called() // wiring up the mocked GenericProcessorFactory to return the correct resource definition SetupProcessorFactoryForResourceDefinition(processorFactory, todoItemResource.Object); var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); - var hookExecutor = new ResourceHookExecutor(context.Object, hooksDiscovery, meta); + var hookExecutor = new ResourceHookExecutor(context.Object, discovery, meta); return (todoItemResource, context, hookExecutor, identifiableTodoItemResource); } (Mock context, IResourceHookExecutor, Mock>, Mock>) - CreateTestObjectsForNestedCase() + CreateTestObjectsForNestedCase( + IImplementedResourceHooks mainDiscovery = null, + IImplementedResourceHooks nestedDiscovery = null + ) where TMain : class, IIdentifiable where TNested : class, IIdentifiable { + mainDiscovery = mainDiscovery ?? new ImplementedResourceHooks(); + nestedDiscovery = nestedDiscovery ?? new ImplementedResourceHooks(); // creates the resource definition mock and corresponding ImplementedHooks discovery instance - (var mainDiscovery, var mainResource, var identifiableMainResource) = CreateResourceDefinition(); - var identifiableNestedResource = CreateResourceDefinition().Item3; + (var mainResource, var identifiableMainResource) = CreateResourceDefinition(mainDiscovery); + var identifiableNestedResource = CreateResourceDefinition(nestedDiscovery).Item2; + // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. (var context, var processorFactory) = CreateContextAndProcessorMocks(); @@ -178,7 +193,7 @@ public void AfterDelete_Hook_Is_Called() Mock> ImplementAs( Mock> resourceDefinition, - ImplementedResourceHooks discovery + IImplementedResourceHooks discovery ) where TImplementAs : class, IIdentifiable where TActual : class, IIdentifiable { @@ -230,17 +245,16 @@ ImplementedResourceHooks discovery return resourceDefinition; } - (ImplementedResourceHooks, Mock>, Mock>) CreateResourceDefinition - () + (Mock>, Mock>) CreateResourceDefinition + (IImplementedResourceHooks discovery) where TModel : class, IIdentifiable { - var discovery = new ImplementedResourceHooks(); var resourceDefinition = new Mock>(); var identifiableResourceDefinition = ImplementAs(resourceDefinition.As>(), discovery); var modelSpecificResourceDefinition = ImplementAs(resourceDefinition.As>(), discovery); - return (discovery, modelSpecificResourceDefinition, identifiableResourceDefinition); + return (modelSpecificResourceDefinition, identifiableResourceDefinition); } From 950c976dd02c7e0bdff05820bd25f451f3f33eda Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 16 Apr 2019 15:59:17 +0200 Subject: [PATCH 023/168] chore: reorganised hooks test --- .../Services/ResourceHookExecutor.cs | 33 +- test/UnitTests/Models/ResourceHookTests.cs | 300 ------------------ .../UnitTests/ResourceHooks/DiscoveryTests.cs | 58 ++++ .../ExecutorFiresCorrectHooksTests.cs | 179 +++++++++++ .../ResourceHooks/ResourceHooksTestBase.cs | 168 ++++++++++ test/UnitTests/UnitTests.csproj | 3 + 6 files changed, 432 insertions(+), 309 deletions(-) delete mode 100644 test/UnitTests/Models/ResourceHookTests.cs create mode 100644 test/UnitTests/ResourceHooks/DiscoveryTests.cs create mode 100644 test/UnitTests/ResourceHooks/ExecutorFiresCorrectHooksTests.cs create mode 100644 test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index 935e9caad7..6aa46b9c6f 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -69,7 +69,6 @@ public virtual IEnumerable AfterCreate(IEnumerable entities, R /// we should still consider to fire the hooks of its relation, eg TodoItem.Owner if (hookContainer == null) return entities; - entities = hookContainer.AfterCreate(entities, actionSource); _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.AfterUpdate); BreadthFirstTraverse(entities, (container, relatedEntities) => @@ -77,6 +76,8 @@ public virtual IEnumerable AfterCreate(IEnumerable entities, R return container.AfterUpdate(relatedEntities, actionSource); }); + entities = hookContainer.AfterCreate(entities, actionSource); + return entities; } @@ -85,9 +86,7 @@ public virtual void BeforeRead(ResourceAction actionSource, string stringId = nu { var hookContainer = _meta.GetResourceHookContainer(ResourceHook.BeforeRead); if (hookContainer == null) return; - - - throw new NotImplementedException(); + hookContainer.BeforeRead(actionSource, stringId); } /// @@ -95,14 +94,14 @@ public virtual IEnumerable AfterRead(IEnumerable entities, Res { var hookContainer = _meta.GetResourceHookContainer(ResourceHook.AfterRead); if (hookContainer == null) return entities; - entities = hookContainer.AfterRead(entities, actionSource); - _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.BeforeUpdate); + _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.AfterRead); BreadthFirstTraverse(entities, (container, relatedEntities) => { - container.BeforeRead(actionSource); return container.AfterRead(relatedEntities, actionSource); }); + entities = hookContainer.AfterRead(entities, actionSource); + return entities; } @@ -112,8 +111,15 @@ public virtual IEnumerable BeforeUpdate(IEnumerable entities, var hookContainer = _meta.GetResourceHookContainer(ResourceHook.BeforeUpdate); if (hookContainer == null) return entities; + entities = hookContainer.BeforeUpdate(entities, actionSource); - throw new NotImplementedException(); + _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.BeforeUpdate); + BreadthFirstTraverse(entities, (container, relatedEntities) => + { + return container.BeforeUpdate(relatedEntities, actionSource); + }); + + return entities; } /// @@ -123,7 +129,16 @@ public virtual IEnumerable AfterUpdate(IEnumerable entities, R if (hookContainer == null) return entities; - throw new NotImplementedException(); + _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.AfterUpdate); + BreadthFirstTraverse(entities, (container, relatedEntities) => + { + return container.AfterUpdate(relatedEntities, actionSource); + }); + + entities = hookContainer.AfterUpdate(entities, actionSource); + + + return entities; } /// diff --git a/test/UnitTests/Models/ResourceHookTests.cs b/test/UnitTests/Models/ResourceHookTests.cs deleted file mode 100644 index d2e68d98b5..0000000000 --- a/test/UnitTests/Models/ResourceHookTests.cs +++ /dev/null @@ -1,300 +0,0 @@ - -using JsonApiDotNetCore.Builders; -using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Generics; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExample.Resources; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using Xunit; - - -namespace UnitTests.Models -{ - public class ResourceHooks_Tests - { - - - public ResourceHooks_Tests() - { - // Build() exposes the static ResourceGraphBuilder.Instance member, which - // is consumed by ResourceDefinition class. - new ResourceGraphBuilder() - .AddResource() - .AddResource() - .Build(); - } - - [Fact] - public void Hook_Discovery() - { - // arrange & act - var hookConfig = new ImplementedResourceHooks(); - // assert - Assert.Contains(ResourceHook.BeforeDelete, hookConfig.ImplementedHooks); - Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); - Assert.Equal(2, hookConfig.ImplementedHooks.Length); - } - - IImplementedResourceHooks SetDiscoverableHooks(ResourceHook[] implementedHooks) - where TEntity : class, IIdentifiable - { - var mock = new Mock>(); - mock.Setup(discovery => discovery.ImplementedHooks) - .Returns(implementedHooks); - return mock.Object; - } - [Fact] - public void BeforeCreate_Hook_Is_Called_With_Nested_BeforeUpdate() - { - // arrange - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - - [Fact] - public void AfterCreate_Hook_Is_Called_With_Nested_AfterUpdate() - { - // arrange - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void BeforeRead_Hook_Is_Called() - { - // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); - // act - hookExecutor.BeforeRead(It.IsAny()); - // assert - resourceDefinitionMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); - } - [Fact] - public void AfterRead_Hook_Is_Called() - { - // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); - // act - hookExecutor.AfterRead(It.IsAny>(), It.IsAny()); - // assert - resourceDefinitionMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void BeforeUpdate_Hook_Is_Called() - { - // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); - // act - hookExecutor.BeforeUpdate(It.IsAny>(), It.IsAny()); - // assert - resourceDefinitionMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void AfterUpdate_Hook_Is_Called() - { - // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); - // act - hookExecutor.AfterUpdate(It.IsAny>(), It.IsAny()); - // assert - resourceDefinitionMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void BeforeDelete_Hook_Is_Called() - { - // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); - - // act - hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); - - // assert - resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void AfterDelete_Hook_Is_Called() - { - // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor, var x) = CreateTestObjectsForSimpleCase(); - // act - hookExecutor.AfterDelete(It.IsAny>(), true, It.IsAny()); - // assert - resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), true, It.IsAny()), Times.Once()); - } - - - (Mock>, Mock, IResourceHookExecutor, Mock>) CreateTestObjectsForSimpleCase() - { - - var discovery = new ImplementedResourceHooks(); - // creates the resource definition mock and corresponding ImplementedHooks discovery instance - (var todoItemResource, var identifiableTodoItemResource) = CreateResourceDefinition(discovery); - - // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. - (var context, var processorFactory) = CreateContextAndProcessorMocks(); - - // wiring up the mocked GenericProcessorFactory to return the correct resource definition - SetupProcessorFactoryForResourceDefinition(processorFactory, todoItemResource.Object); - var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); - var hookExecutor = new ResourceHookExecutor(context.Object, discovery, meta); - return (todoItemResource, context, hookExecutor, identifiableTodoItemResource); - } - - (Mock context, IResourceHookExecutor, Mock>, Mock>) - CreateTestObjectsForNestedCase( - IImplementedResourceHooks mainDiscovery = null, - IImplementedResourceHooks nestedDiscovery = null - ) - where TMain : class, IIdentifiable - where TNested : class, IIdentifiable - { - mainDiscovery = mainDiscovery ?? new ImplementedResourceHooks(); - nestedDiscovery = nestedDiscovery ?? new ImplementedResourceHooks(); - - // creates the resource definition mock and corresponding ImplementedHooks discovery instance - (var mainResource, var identifiableMainResource) = CreateResourceDefinition(mainDiscovery); - var identifiableNestedResource = CreateResourceDefinition(nestedDiscovery).Item2; - - // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. - (var context, var processorFactory) = CreateContextAndProcessorMocks(); - - SetupProcessorFactoryForResourceDefinition(processorFactory, mainResource.Object); - var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); - var hookExecutor = new ResourceHookExecutor(context.Object, mainDiscovery, meta); - - SetupProcessorFactoryForResourceDefinition(processorFactory, identifiableNestedResource.Object); - - return (context, hookExecutor, mainResource, identifiableNestedResource); - } - - Mock> ImplementAs( - Mock> resourceDefinition, - IImplementedResourceHooks discovery - ) where TImplementAs : class, IIdentifiable - where TActual : class, IIdentifiable - { - resourceDefinition - .As>() - .Setup(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny())) - .Returns, ResourceAction>((entities, action) => entities) - .Verifiable(); - resourceDefinition - .As>() - .Setup(rd => rd.AfterCreate(It.IsAny>(), It.IsAny())) - .Returns, ResourceAction>((entities, action) => entities) - .Verifiable(); - resourceDefinition - .As>() - .Setup(rd => rd.BeforeRead(It.IsAny(), null)) - .Verifiable(); - resourceDefinition - .As>() - .Setup(rd => rd.AfterRead(It.IsAny>(), It.IsAny())) - .Returns, ResourceAction>((entities, action) => entities) - .Verifiable(); - resourceDefinition - .As>() - .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) - .Returns, ResourceAction>((entities, action) => entities) - .Verifiable(); - resourceDefinition - .As>() - .Setup(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny())) - .Returns, ResourceAction>((entities, action) => entities) - .Verifiable(); - resourceDefinition - .As>() - .Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())) - .Verifiable(); - resourceDefinition - .As>() - .Setup(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny())) - .Verifiable(); - resourceDefinition - .As>() - .Setup(rd => rd.ShouldExecuteHook(It.IsAny())) - .Returns((hook) => discovery.ImplementedHooks.Contains(hook)); - resourceDefinition - .As>() - .Setup(rd => rd.ShouldExecuteHook(It.IsAny())) - .Returns((hook) => discovery.ImplementedHooks.Contains(hook)); - return resourceDefinition; - } - - (Mock>, Mock>) CreateResourceDefinition - (IImplementedResourceHooks discovery) - where TModel : class, IIdentifiable - { - var resourceDefinition = new Mock>(); - - var identifiableResourceDefinition = ImplementAs(resourceDefinition.As>(), discovery); - var modelSpecificResourceDefinition = ImplementAs(resourceDefinition.As>(), discovery); - - return (modelSpecificResourceDefinition, identifiableResourceDefinition); - } - - - - (Mock, Mock) CreateContextAndProcessorMocks() - { - var processorFactory = new Mock(); - var context = new Mock(); - context.Setup(c => c.GenericProcessorFactory).Returns(processorFactory.Object); - return (context, processorFactory); - } - - - void SetupProcessorFactoryForResourceDefinition( - Mock processorFactory, - IResourceHookContainer modelResource) - where TModel : class, IIdentifiable - where TInnerContainerType : class, IIdentifiable - - { - processorFactory.Setup(c => c.GetProcessor(typeof(IResourceHookContainer<>), typeof(TModel))) - .Returns(modelResource); - } - - } - - public class Dummy : Identifiable - { - - } - public class DummyResourceDefinition : ResourceDefinition - { - public override void BeforeDelete(IEnumerable entities, ResourceAction actionSource) - { - } - public override void AfterDelete(IEnumerable entities, bool succeeded, ResourceAction actionSource) - { - - } - } - - -} diff --git a/test/UnitTests/ResourceHooks/DiscoveryTests.cs b/test/UnitTests/ResourceHooks/DiscoveryTests.cs new file mode 100644 index 0000000000..f4adc9066a --- /dev/null +++ b/test/UnitTests/ResourceHooks/DiscoveryTests.cs @@ -0,0 +1,58 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + public class DiscoveryTests + { + + public DiscoveryTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + [Fact] + public void Hook_Discovery() + { + // arrange & act + var hookConfig = new ImplementedResourceHooks(); + // assert + Assert.Contains(ResourceHook.BeforeDelete, hookConfig.ImplementedHooks); + Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); + + } + public class Dummy : Identifiable + { + + } + public class DummyResourceDefinition : ResourceDefinition + { + public override void BeforeDelete(IEnumerable entities, ResourceAction actionSource) + { + } + public override void AfterDelete(IEnumerable entities, bool succeeded, ResourceAction actionSource) + { + + } + } + } + + +} diff --git a/test/UnitTests/ResourceHooks/ExecutorFiresCorrectHooksTests.cs b/test/UnitTests/ResourceHooks/ExecutorFiresCorrectHooksTests.cs new file mode 100644 index 0000000000..29bbf6145a --- /dev/null +++ b/test/UnitTests/ResourceHooks/ExecutorFiresCorrectHooksTests.cs @@ -0,0 +1,179 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class ExecutorFiresCorrectHooksTests : ResourceHooksTestBase + { + public ExecutorFiresCorrectHooksTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + + [Fact] + public void BeforeCreate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + + [Fact] + public void AfterCreate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + [Fact] + public void BeforeRead() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeRead(It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + } + [Fact] + public void AfterRead() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + + } + [Fact] + public void BeforeUpdate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeUpdate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + [Fact] + public void AfterUpdate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterUpdate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + [Fact] + public void BeforeDelete() + { + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(); + + // act + hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); + + // assert + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); + } + [Fact] + public void AfterDelete() + { + // arrange + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(); + // act + hookExecutor.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Once()); + } + + + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs new file mode 100644 index 0000000000..a70dd8f7df --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs @@ -0,0 +1,168 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class ResourceHooksTestBase + { + protected (Mock>, Mock, IResourceHookExecutor) CreateTestObjectsForSimpleCase() + { + var discovery = SetDiscoverableHooks(); + // creates the resource definition mock and corresponding ImplementedHooks discovery instance + (var todoItemResource, var identifiableTodoItemResource) = CreateResourceDefinition(discovery); + + // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + (var context, var processorFactory) = CreateContextAndProcessorMocks(); + + // wiring up the mocked GenericProcessorFactory to return the correct resource definition + SetupProcessorFactoryForResourceDefinition(processorFactory, todoItemResource.Object); + var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); + var hookExecutor = new ResourceHookExecutor(context.Object, discovery, meta); + return (todoItemResource, context, hookExecutor); + } + + protected (Mock context, IResourceHookExecutor, Mock>, Mock>) + CreateTestObjectsForNestedCase( + IImplementedResourceHooks mainDiscovery = null, + IImplementedResourceHooks nestedDiscovery = null + ) + where TMain : class, IIdentifiable + where TNested : class, IIdentifiable + { + // creates the resource definition mock and corresponding for a given set of discoverable hooks + (var mainResource, var identifiableMainResource) = CreateResourceDefinition(mainDiscovery); + var identifiableNestedResource = CreateResourceDefinition(nestedDiscovery).Item2; + + // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + (var context, var processorFactory) = CreateContextAndProcessorMocks(); + + SetupProcessorFactoryForResourceDefinition(processorFactory, mainResource.Object); + var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); + var hookExecutor = new ResourceHookExecutor(context.Object, mainDiscovery, meta); + + SetupProcessorFactoryForResourceDefinition(processorFactory, identifiableNestedResource.Object); + + return (context, hookExecutor, mainResource, identifiableNestedResource); + } + + + protected IImplementedResourceHooks SetDiscoverableHooks(ResourceHook[] implementedHooks = null) + where TEntity : class, IIdentifiable + { + implementedHooks = implementedHooks ?? Enum.GetValues(typeof(ResourceHook)) + .Cast() + .Where(h => h != ResourceHook.None) + .ToArray(); + var mock = new Mock>(); + mock.Setup(discovery => discovery.ImplementedHooks) + .Returns(implementedHooks); + return mock.Object; + } + + private Mock> ImplementAs( + Mock> resourceDefinition, + IImplementedResourceHooks discovery + ) where TImplementAs : class, IIdentifiable + where TActual : class, IIdentifiable + { + resourceDefinition + .As>() + .Setup(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.AfterCreate(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.BeforeRead(It.IsAny(), null)) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.AfterRead(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny())) + .Returns, ResourceAction>((entities, action) => entities) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny())) + .Verifiable(); + resourceDefinition + .As>() + .Setup(rd => rd.ShouldExecuteHook(It.IsAny())) + .Returns((hook) => discovery.ImplementedHooks.Contains(hook)); + resourceDefinition + .As>() + .Setup(rd => rd.ShouldExecuteHook(It.IsAny())) + .Returns((hook) => discovery.ImplementedHooks.Contains(hook)); + return resourceDefinition; + } + + + private (Mock>, Mock>) CreateResourceDefinition + (IImplementedResourceHooks discovery) + where TModel : class, IIdentifiable + { + var resourceDefinition = new Mock>(); + + var identifiableResourceDefinition = ImplementAs(resourceDefinition.As>(), discovery); + var modelSpecificResourceDefinition = ImplementAs(resourceDefinition.As>(), discovery); + + return (modelSpecificResourceDefinition, identifiableResourceDefinition); + } + + + + private (Mock, Mock) CreateContextAndProcessorMocks() + { + var processorFactory = new Mock(); + var context = new Mock(); + context.Setup(c => c.GenericProcessorFactory).Returns(processorFactory.Object); + return (context, processorFactory); + } + + + private void SetupProcessorFactoryForResourceDefinition( + Mock processorFactory, + IResourceHookContainer modelResource) + where TModel : class, IIdentifiable + where TInnerContainerType : class, IIdentifiable + + { + processorFactory.Setup(c => c.GetProcessor(typeof(IResourceHookContainer<>), typeof(TModel))) + .Returns(modelResource); + } + + } + + +} + diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index b88e85582a..058f317f06 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -19,4 +19,7 @@ PreserveNewest + + + From 40ecd5ec12082528c90b001e6c6b1ecc8c5a5219 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 17 Apr 2019 10:18:56 +0200 Subject: [PATCH 024/168] feat: hook response validation, some more tests --- src/JsonApiDotNetCore/Internal/TypeHelper.cs | 10 + .../Services/EntityResourceService.cs | 8 +- .../Services/IResourceHookExecutor.cs | 3 +- .../Services/ResourceHookExecutor.cs | 95 +++- .../Services/ResourceHookMetaInfo.cs | 108 ++++- .../ExecutorFiresCorrectHooksTests.cs | 179 ------- .../ResourceHookExecutorTests.cs | 444 ++++++++++++++++++ .../ResourceHooks/ResourceHooksTestBase.cs | 16 +- 8 files changed, 634 insertions(+), 229 deletions(-) delete mode 100644 test/UnitTests/ResourceHooks/ExecutorFiresCorrectHooksTests.cs create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutorTests.cs diff --git a/src/JsonApiDotNetCore/Internal/TypeHelper.cs b/src/JsonApiDotNetCore/Internal/TypeHelper.cs index 454741ff03..74b651a0dd 100644 --- a/src/JsonApiDotNetCore/Internal/TypeHelper.cs +++ b/src/JsonApiDotNetCore/Internal/TypeHelper.cs @@ -94,5 +94,15 @@ public static IList CreateListFor(Type type) return list; } + + /// + /// Gets the generic argument T of List{T} + /// + /// The type of the list + /// The list to be inspected/param> + public static Type GetListInnerType(IEnumerable list) + { + return list.GetType().GetGenericArguments()[0]; + } } } diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 679d6b1446..feef15fc21 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -80,7 +80,7 @@ public virtual async Task CreateAsync(TResource resource) { var entity = MapIn(resource); // @TODO implement hook executor - // entity = _hookExecutor.BeforeCreate(AsList(entity), ResourceAction.Create).SingleOrDefault(); + entity = _hookExecutor.BeforeCreate(AsList(entity), ResourceAction.Create).SingleOrDefault(); entity = await _entities.CreateAsync(entity); @@ -96,7 +96,7 @@ public virtual async Task CreateAsync(TResource resource) } // @TODO implement hook executor - // entity = _hookExecutor.AfterCreate(AsList(entity), ResourceAction.Create).SingleOrDefault(); + entity = _hookExecutor.AfterCreate(AsList(entity), ResourceAction.Create).SingleOrDefault(); return MapOut(entity); } @@ -119,7 +119,7 @@ public virtual async Task DeleteAsync(TId id) public virtual async Task> GetAsync() { // @TODO implement hook executor - // _hookExecutor.BeforeRead(ResourceAction.Get); + //_hookExecutor.BeforeRead(ResourceAction.Get); var entities = _entities.Get(); entities = ApplySortAndFilterQuery(entities); @@ -128,7 +128,7 @@ public virtual async Task> GetAsync() entities = IncludeRelationships(entities, _jsonApiContext.QuerySet.IncludedRelationships); // @TODO implement hook executor - // entities = _hookExecutor.AfterRead(entities, ResourceAction.Get); + //entities = _hookExecutor.AfterRead(entities, ResourceAction.Get); // note: The hookexecutor will also fire the BeforeRead and AfterRead hooks // for every included entity. diff --git a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs index 3916d90f40..adf849e155 100644 --- a/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/IResourceHookExecutor.cs @@ -11,12 +11,13 @@ namespace JsonApiDotNetCore.Services /// public enum ResourceAction { + None, Get, GetSingle, GetRelationship, Create, Patch, - PatchRelationships, + PatchRelationship, Delete } diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index 6aa46b9c6f..27460c7424 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using JsonApiDotNetCore.Internal; @@ -20,6 +21,7 @@ public class ResourceHookExecutor : IResourceHookExecutor wher protected readonly IResourceGraph _graph; protected readonly Type _entityType; protected readonly IResourceHookMetaInfo _meta; + protected readonly ResourceAction[] _singleActions; private ResourceHook _hookInTreeTraversal; @@ -35,17 +37,32 @@ IResourceHookMetaInfo meta _meta = meta; _implementedHooks = hooksConfiguration.ImplementedHooks; _entityType = typeof(TEntity); + _singleActions = new ResourceAction[] + { + ResourceAction.GetSingle, + ResourceAction.Create, + ResourceAction.Delete, + ResourceAction.Patch, + ResourceAction.GetRelationship, + ResourceAction.PatchRelationship + }; } public virtual IEnumerable BeforeCreate(IEnumerable entities, ResourceAction actionSource) { var hookContainer = _meta.GetResourceHookContainer(ResourceHook.BeforeCreate); - if (hookContainer == null) return entities; + /// traversing the 0th layer. Not including this in the recursive function /// because the most complexities that arrise in the tree traversal do not /// apply to the 0th layer (eg non-homogeneity of the next layers) - entities = hookContainer.BeforeCreate(entities, actionSource); // eg all of type {Article} + if (hookContainer != null) + { + var parsedEntities = hookContainer.BeforeCreate(entities, actionSource); // eg all of type {Article} + ValidateHookResponse(entities, parsedEntities, actionSource); + entities = parsedEntities; + } + /// We use IIdentifiable instead of TEntity, because deeper layers /// in the tree traversal will not necessarily be homogenous (i.e. @@ -67,8 +84,6 @@ public virtual IEnumerable AfterCreate(IEnumerable entities, R var hookContainer = _meta.GetResourceHookContainer(ResourceHook.AfterCreate); /// @TODO: even if we don't have an implementation for eg TodoItem AfterCreate, /// we should still consider to fire the hooks of its relation, eg TodoItem.Owner - if (hookContainer == null) return entities; - _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.AfterUpdate); BreadthFirstTraverse(entities, (container, relatedEntities) => @@ -76,7 +91,12 @@ public virtual IEnumerable AfterCreate(IEnumerable entities, R return container.AfterUpdate(relatedEntities, actionSource); }); - entities = hookContainer.AfterCreate(entities, actionSource); + if (hookContainer != null) + { + var parsedEntities = hookContainer.AfterCreate(entities, actionSource); + ValidateHookResponse(entities, parsedEntities, actionSource); + return parsedEntities; + } return entities; } @@ -93,25 +113,39 @@ public virtual void BeforeRead(ResourceAction actionSource, string stringId = nu public virtual IEnumerable AfterRead(IEnumerable entities, ResourceAction actionSource) { var hookContainer = _meta.GetResourceHookContainer(ResourceHook.AfterRead); - if (hookContainer == null) return entities; - _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.AfterRead); + _meta.UpdateMetaInformation(new Type[] { _entityType }, new ResourceHook[] { ResourceHook.AfterRead, ResourceHook.BeforeRead }); BreadthFirstTraverse(entities, (container, relatedEntities) => { - return container.AfterRead(relatedEntities, actionSource); + if (container.ShouldExecuteHook(ResourceHook.BeforeRead)) container.BeforeRead(actionSource); + if (container.ShouldExecuteHook(ResourceHook.AfterRead)) + { + var parsedEntities = container.AfterRead(relatedEntities, actionSource); + ValidateHookResponse(relatedEntities, parsedEntities); + return parsedEntities; + } + return relatedEntities; }); - entities = hookContainer.AfterRead(entities, actionSource); + if (hookContainer != null) + { + var parsedEntities = hookContainer.AfterRead(entities, actionSource); + ValidateHookResponse(entities, parsedEntities, actionSource); + return parsedEntities; + } + return entities; } - /// public virtual IEnumerable BeforeUpdate(IEnumerable entities, ResourceAction actionSource) { var hookContainer = _meta.GetResourceHookContainer(ResourceHook.BeforeUpdate); - if (hookContainer == null) return entities; - - entities = hookContainer.BeforeUpdate(entities, actionSource); + if (hookContainer != null) + { + var parsedEntities = hookContainer.BeforeUpdate(entities, actionSource); + ValidateHookResponse(entities, parsedEntities, actionSource); + entities = parsedEntities; + } _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.BeforeUpdate); BreadthFirstTraverse(entities, (container, relatedEntities) => @@ -126,8 +160,6 @@ public virtual IEnumerable BeforeUpdate(IEnumerable entities, public virtual IEnumerable AfterUpdate(IEnumerable entities, ResourceAction actionSource) { var hookContainer = _meta.GetResourceHookContainer(ResourceHook.AfterUpdate); - if (hookContainer == null) return entities; - _meta.UpdateMetaInformation(new Type[] { _entityType }, ResourceHook.AfterUpdate); BreadthFirstTraverse(entities, (container, relatedEntities) => @@ -135,8 +167,12 @@ public virtual IEnumerable AfterUpdate(IEnumerable entities, R return container.AfterUpdate(relatedEntities, actionSource); }); - entities = hookContainer.AfterUpdate(entities, actionSource); - + if (hookContainer != null) + { + var parsedEntities = hookContainer.AfterUpdate(entities, actionSource); + ValidateHookResponse(entities, parsedEntities, actionSource); + return parsedEntities; + } return entities; } @@ -146,7 +182,6 @@ public virtual void BeforeDelete(IEnumerable entities, ResourceAction a { var hookContainer = _meta.GetResourceHookContainer(ResourceHook.BeforeDelete); if (hookContainer == null) return; - hookContainer.BeforeDelete(entities, actionSource); } @@ -155,10 +190,32 @@ public virtual void AfterDelete(IEnumerable entities, bool succeeded, R { var hookContainer = _meta.GetResourceHookContainer(ResourceHook.AfterDelete); if (hookContainer == null) return; - hookContainer.AfterDelete(entities, succeeded, actionSource); } + /// + /// Ensures that the return type from the hook matches the required type. + /// And when relevant, eg. AfterRead when fired from GetAsync(TId id), checks that the collection + /// does not contain more than one item. + /// + /// The initial collection before the hook was executed. + /// The collection returned from the hook + /// The pipeine from which the hook was fired + protected void ValidateHookResponse(IEnumerable initalList, IEnumerable returnedList, ResourceAction actionSource = 0) + { + if (TypeHelper.GetListInnerType(initalList as IEnumerable) != TypeHelper.GetListInnerType(returnedList as IEnumerable)) + { + throw new ApplicationException("The List type of the return value from a resource hook" + + "did not match the the same type as the original collection. Make sure you are returning collections of the same" + + "entities as recieved from your hooks"); + } + if (actionSource != ResourceAction.None && _singleActions.Contains(actionSource) && returnedList.Count() > 1) + { + throw new ApplicationException("The returned collection from this hook may only contain one item in the case of he" + + actionSource.ToString() + "pipeline"); + } + } + /// /// Fires the hooks for related (nested) entities. /// diff --git a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs index ec13d7407b..6f25f48247 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs @@ -13,6 +13,7 @@ public interface IResourceHookMetaInfo IResourceHookContainer GetResourceHookContainer(Type targetEntity, ResourceHook hook = ResourceHook.None); IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable; Dictionary UpdateMetaInformation(IEnumerable nextLayerTypes, ResourceHook hook = ResourceHook.None); + Dictionary UpdateMetaInformation(IEnumerable nextLayerTypes, IEnumerable hooks); } public class ResourceHookMetaInfo : IResourceHookMetaInfo @@ -21,7 +22,7 @@ public class ResourceHookMetaInfo : IResourceHookMetaInfo protected readonly IGenericProcessorFactory _genericProcessorFactory; protected readonly IResourceGraph _graph; protected readonly Dictionary> _hookContainers; - protected ResourceHook _hookInTreeTraversal; + protected readonly List _targetedHooksForRelatedEntities; protected Dictionary _meta; public ResourceHookMetaInfo( @@ -33,6 +34,7 @@ IResourceGraph graph _graph = graph; _meta = new Dictionary(); _hookContainers = new Dictionary>(); + _targetedHooksForRelatedEntities = new List(); } @@ -65,15 +67,37 @@ public IEnumerable GetMetaEntries(IIdentifiable currentLa /// The hook to get a ResourceDefinition for. public IResourceHookContainer GetResourceHookContainer(Type targetEntity, ResourceHook hook = ResourceHook.None) { - hook = (hook == ResourceHook.None) ? _hookInTreeTraversal : hook; + /// 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(targetEntity, out IResourceHookContainer container)) { + container = (IResourceHookContainer)_genericProcessorFactory.GetProcessor(typeof(IResourceHookContainer<>), targetEntity); _hookContainers[targetEntity] = container; } if (container == null) return container; - if (!container.ShouldExecuteHook(hook)) container = null; - return container; + + /// if there was a container, first check if it implements the hook we + /// want to use it for. + List targetHooks; + if (hook == ResourceHook.None) + { + CheckForTargetHookExistence(); + targetHooks = _targetedHooksForRelatedEntities; + } + else + { + targetHooks = new List() { hook }; + } + + foreach (ResourceHook targetHook in targetHooks) + { + if (container.ShouldExecuteHook(targetHook)) return container; + } + return null; + } /// @@ -92,6 +116,8 @@ public IResourceHookContainer GetResourceHookContainer(Resourc return (IResourceHookContainer)GetResourceHookContainer(typeof(TEntity), hook); } + + /// /// Creates a (helper) dictionary containing meta information needed for /// the traversal of the next layer. It contains as @@ -103,33 +129,75 @@ public IResourceHookContainer GetResourceHookContainer(Resourc /// /// The meta dict. /// Unique list of types to extract metadata from - /// The target resource hook type + /// The target resource hook types public Dictionary UpdateMetaInformation( IEnumerable nextLayerTypes, - ResourceHook hook = ResourceHook.None) + IEnumerable hooks) { + + if (hooks == null || !hooks.Any()) + { + CheckForTargetHookExistence(); + hooks = _targetedHooksForRelatedEntities; + } + else + { + if (!_targetedHooksForRelatedEntities.Any()) + _targetedHooksForRelatedEntities.AddRange(hooks); + } - _hookInTreeTraversal = _hookInTreeTraversal != - ResourceHook.None ? - _hookInTreeTraversal : - hook; - foreach (Type targetType in nextLayerTypes) + foreach (ResourceHook targetHook in hooks) { - var contextEntity = _graph.GetContextEntity(targetType); - var relationshipsForContextEntity = contextEntity.Relationships.ToDictionary( - attr => CreateMetaKey(attr, targetType, checkForDuplicates: true), - attr => attr); - /// keep only the meta info we really need for the traversal of the next layer - /// also remove duplicates. - PruneMetaDictionary(relationshipsForContextEntity, _hookInTreeTraversal); - _meta = _meta.Concat(relationshipsForContextEntity) - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + foreach (Type targetType in nextLayerTypes) + { + var contextEntity = _graph.GetContextEntity(targetType); + var relationshipsForContextEntity = contextEntity.Relationships.ToDictionary( + attr => CreateMetaKey(attr, targetType, checkForDuplicates: true), + attr => attr); + /// keep only the meta info we really need for the traversal of the next layer + /// also remove duplicates. + PruneMetaDictionary(relationshipsForContextEntity, targetHook); + _meta = _meta.Concat(relationshipsForContextEntity) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } } + + return _meta; + } + void CheckForTargetHookExistence() + { + if (!_targetedHooksForRelatedEntities.Any()) + throw new InvalidOperationException("Something is not right in the breadth first traversal of resource hook: " + + "trying to get meta information when no allowed hooks are set"); + } + + /// + /// Creates a (helper) dictionary containing meta information needed for + /// the traversal of the next layer. It contains as + /// keys: Type, namely typeof(TRelatedType) that will occur in the traversal + /// of the next layer, + /// values: a Tuple of + /// * RelationshipAttribute (that contains getters and setters) + /// * IResourceHookExecutor{TRelatedType} to access the actual (nested) hook + /// + /// The meta dict. + /// Unique list of types to extract metadata from + /// The target resource hook type + public Dictionary + UpdateMetaInformation( + IEnumerable nextLayerTypes, + ResourceHook hook = ResourceHook.None) + { + var targetHooks = (hook == ResourceHook.None) ? _targetedHooksForRelatedEntities : new List { hook }; + return UpdateMetaInformation(nextLayerTypes, targetHooks); + } + + /// /// Creates the key for the meta dict. The RelationshipAttribute that is diff --git a/test/UnitTests/ResourceHooks/ExecutorFiresCorrectHooksTests.cs b/test/UnitTests/ResourceHooks/ExecutorFiresCorrectHooksTests.cs deleted file mode 100644 index 29bbf6145a..0000000000 --- a/test/UnitTests/ResourceHooks/ExecutorFiresCorrectHooksTests.cs +++ /dev/null @@ -1,179 +0,0 @@ - -using JsonApiDotNetCore.Builders; -using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Generics; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExample.Resources; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using Xunit; - - -namespace UnitTests.ResourceHooks -{ - - public class ExecutorFiresCorrectHooksTests : ResourceHooksTestBase - { - public ExecutorFiresCorrectHooksTests() - { - // Build() exposes the static ResourceGraphBuilder.Instance member, which - // is consumed by ResourceDefinition class. - new ResourceGraphBuilder() - .AddResource() - .AddResource() - .Build(); - } - - - [Fact] - public void BeforeCreate() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - - [Fact] - public void AfterCreate() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void BeforeRead() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeRead(It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); - } - [Fact] - public void AfterRead() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterRead(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); - - } - [Fact] - public void BeforeUpdate() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeUpdate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void AfterUpdate() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterUpdate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterUpdate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void BeforeDelete() - { - // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(); - - // act - hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); - - // assert - resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void AfterDelete() - { - // arrange - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(); - // act - hookExecutor.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()); - // assert - resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Once()); - } - - - } -} - diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutorTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutorTests.cs new file mode 100644 index 0000000000..a041f4d90b --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutorTests.cs @@ -0,0 +1,444 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class ResourceHookExectuorTests : ResourceHooksTestBase + { + public ResourceHookExectuorTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + + [Fact] + public void BeforeCreate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + + [Fact] + public void BeforeCreate_Without_Parent_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + [Fact] + public void BeforeCreate_Without_Child_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Never()); + } + [Fact] + public void BeforeCreate_Without_Any_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Never()); + } + + [Fact] + public void AfterCreate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + + [Fact] + public void AfterCreate_Without_Parent_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + + [Fact] + public void AfterCreate_Without_Child_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Never()); + } + + [Fact] + public void AfterCreate_Without_Any_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Never()); + } + + [Fact] + public void BeforeRead() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeRead(It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + } + [Fact] + public void AfterRead() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny() ), Times.Once()); + } + + [Fact] + public void AfterRead_Without_Parent_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + } + + + [Fact] + public void AfterRead_Without_Child_Before_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.AfterRead }); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); + } + [Fact] + public void AfterRead_Without_Child_After_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.BeforeRead }); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + } + + [Fact] + public void AfterRead_Without_Any_Child_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); + } + [Fact] + public void AfterRead_Without_Any_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); + } + + [Fact] + public void BeforeUpdate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeUpdate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + [Fact] + public void AfterUpdate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterUpdate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + } + [Fact] + public void BeforeDelete() + { + // arrange + var discovery = SetDiscoverableHooks(); + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(discovery); + + // act + hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); + + // assert + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); + } + + [Fact] + public void BeforeDelete_Without_Any_Hook_Implemented() + { + // arrange + var discovery = SetDiscoverableHooks(new ResourceHook[0]); + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(discovery); + + // act + hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); + + // assert + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Never()); + } + + + [Fact] + public void AfterDelete() + { + // arrange + var discovery = SetDiscoverableHooks(); + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(discovery); + // act + hookExecutor.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Once()); + } + + [Fact] + public void AfterDelete_Without_Any_Hook_Implemented() + { + // arrange + var discovery = SetDiscoverableHooks(new ResourceHook[0]); + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(discovery); + // act + hookExecutor.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never()); + } + + + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs index a70dd8f7df..c545185991 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs @@ -18,9 +18,10 @@ namespace UnitTests.ResourceHooks public class ResourceHooksTestBase { - protected (Mock>, Mock, IResourceHookExecutor) CreateTestObjectsForSimpleCase() + protected (Mock>, Mock, IResourceHookExecutor) + CreateTestObjectsForSimpleCase(IImplementedResourceHooks discovery) { - var discovery = SetDiscoverableHooks(); + // creates the resource definition mock and corresponding ImplementedHooks discovery instance (var todoItemResource, var identifiableTodoItemResource) = CreateResourceDefinition(discovery); @@ -37,14 +38,15 @@ public class ResourceHooksTestBase protected (Mock context, IResourceHookExecutor, Mock>, Mock>) CreateTestObjectsForNestedCase( IImplementedResourceHooks mainDiscovery = null, - IImplementedResourceHooks nestedDiscovery = null + IImplementedResourceHooks nestedDiscovery = null, + List> orderOfExecution = null ) where TMain : class, IIdentifiable where TNested : class, IIdentifiable { // creates the resource definition mock and corresponding for a given set of discoverable hooks - (var mainResource, var identifiableMainResource) = CreateResourceDefinition(mainDiscovery); - var identifiableNestedResource = CreateResourceDefinition(nestedDiscovery).Item2; + (var mainResource, var identifiableMainResource) = CreateResourceDefinition(mainDiscovery, orderOfExecution); + var identifiableNestedResource = CreateResourceDefinition(nestedDiscovery, orderOfExecution).Item2; // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. (var context, var processorFactory) = CreateContextAndProcessorMocks(); @@ -128,7 +130,9 @@ IImplementedResourceHooks discovery private (Mock>, Mock>) CreateResourceDefinition - (IImplementedResourceHooks discovery) + (IImplementedResourceHooks discovery, + List> orderOfExecution = null + ) where TModel : class, IIdentifiable { var resourceDefinition = new Mock>(); From 383d15d9ccbc8243fcd7b73c43bb29a1a9ea30c5 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 17 Apr 2019 12:16:40 +0200 Subject: [PATCH 025/168] chore: compatability with prior tests, tests reorganized --- .../Models/ArticleTag.cs | 4 +- .../Services/ResourceHookExecutor.cs | 3 + .../Services/ResourceHookMetaInfo.cs | 3 +- .../ResourceHookExecutor/AfterCreateTests.cs | 130 +++++ .../ResourceHookExecutor/AfterDeleteTests.cs | 61 +++ .../ResourceHookExecutor/AfterReadTests.cs | 162 +++++++ .../ResourceHookExecutor/AfterUpdateTests.cs | 131 ++++++ .../ResourceHookExecutor/BeforeCreateTests.cs | 134 ++++++ .../ResourceHookExecutor/BeforeDeleteTests.cs | 65 +++ .../ResourceHookExecutor/BeforeReadTests.cs | 57 +++ .../ResourceHookExecutor/BeforeUpdateTests.cs | 136 ++++++ .../ResourceHookExecutorTests.cs | 444 ------------------ .../ResourceHooks/ResourceHooksTestBase.cs | 69 ++- test/UnitTests/UnitTests.csproj | 1 + 14 files changed, 937 insertions(+), 463 deletions(-) create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterCreateTests.cs create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterDeleteTests.cs create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterUpdateTests.cs create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeCreateTests.cs create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeDeleteTests.cs create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeReadTests.cs create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeUpdateTests.cs delete mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutorTests.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/ArticleTag.cs b/src/Examples/JsonApiDotNetCoreExample/Models/ArticleTag.cs index 992e688c51..2e45ea84ae 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/ArticleTag.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/ArticleTag.cs @@ -1,6 +1,8 @@ +using JsonApiDotNetCore.Models; + namespace JsonApiDotNetCoreExample.Models { - public class ArticleTag + public class ArticleTag : Identifiable { public int ArticleId { get; set; } public Article Article { get; set; } diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index 27460c7424..e685196df5 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -228,6 +228,9 @@ Func, IEnumerable, IEnumera { // for the entities in the current layer: get the collection of all related entities var relationshipsInCurrentLayer = ExtractionLoop(currentLayer); + + if (!relationshipsInCurrentLayer.Any()) return; + // for the unique set of entities in that collection, execute the hooks ExecutionLoop(relationshipsInCurrentLayer, hookExecutionAction); // for the entities in the current layer: reassign relationships where needed. diff --git a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs index 6f25f48247..1b94d04136 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs @@ -73,8 +73,7 @@ public IResourceHookContainer GetResourceHookContainer(Type targe /// so we need not even bother. if (!_hookContainers.TryGetValue(targetEntity, out IResourceHookContainer container)) { - - container = (IResourceHookContainer)_genericProcessorFactory.GetProcessor(typeof(IResourceHookContainer<>), targetEntity); + container = (_genericProcessorFactory.GetProcessor(typeof(IResourceHookContainer<>), targetEntity)) as IResourceHookContainer; _hookContainers[targetEntity] = container; } if (container == null) return container; diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterCreateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterCreateTests.cs new file mode 100644 index 0000000000..55890d2c9a --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterCreateTests.cs @@ -0,0 +1,130 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class AfterCreateTests : ResourceHooksTestBase + { + public AfterCreateTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + [Fact] + public void AfterCreate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void AfterCreate_Without_Parent_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterCreate(todoInput, It.IsAny()); + // assert + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + + } + + [Fact] + public void AfterCreate_Without_Child_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void AfterCreate_Without_Any_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterDeleteTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterDeleteTests.cs new file mode 100644 index 0000000000..87fac11886 --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterDeleteTests.cs @@ -0,0 +1,61 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class AfterDeleteTests : ResourceHooksTestBase + { + public AfterDeleteTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + + [Fact] + public void AfterDelete() + { + // arrange + var discovery = SetDiscoverableHooks(); + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(discovery); + // act + hookExecutor.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()); + // assert + resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Once()); + resourceDefinitionMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + resourceDefinitionMock.VerifyNoOtherCalls(); + + } + + [Fact] + public void AfterDelete_Without_Any_Hook_Implemented() + { + // arrange + var discovery = SetDiscoverableHooks(new ResourceHook[0]); + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(discovery); + // act + hookExecutor.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()); + // assert + resourceDefinitionMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + resourceDefinitionMock.VerifyNoOtherCalls(); + } + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs new file mode 100644 index 0000000000..745e70efbc --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs @@ -0,0 +1,162 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class AfterReadTests : ResourceHooksTestBase + { + public AfterReadTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + public void AfterRead() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + } + + [Fact] + public void AfterRead_Without_Parent_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + } + + + [Fact] + public void AfterRead_Without_Child_Before_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.AfterRead }); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); + } + [Fact] + public void AfterRead_Without_Child_After_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.BeforeRead }); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + } + + [Fact] + public void AfterRead_Without_Any_Child_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); + } + [Fact] + public void AfterRead_Without_Any_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterRead(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); + } + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterUpdateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterUpdateTests.cs new file mode 100644 index 0000000000..cdc13b1375 --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterUpdateTests.cs @@ -0,0 +1,131 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class AfterUpdateTests : ResourceHooksTestBase + { + public AfterUpdateTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + [Fact] + public void AfterUpdate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterUpdate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void AfterUpdate_Without_Parent_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterUpdate(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void AfterUpdate_Without_Child_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.AfterUpdate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Never()); + + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void AfterUpdate_Without_Any_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.AfterUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeCreateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeCreateTests.cs new file mode 100644 index 0000000000..5387cef084 --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeCreateTests.cs @@ -0,0 +1,134 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class BeforeCreateTests : ResourceHooksTestBase + { + public BeforeCreateTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + + + [Fact] + public void BeforeCreate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void BeforeCreate_Without_Parent_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + [Fact] + public void BeforeCreate_Without_Child_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + [Fact] + public void BeforeCreate_Without_Any_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeCreate(todoInput, It.IsAny()); + // assert + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + + + + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeDeleteTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeDeleteTests.cs new file mode 100644 index 0000000000..61e60ca49d --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeDeleteTests.cs @@ -0,0 +1,65 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class BeforeDeleteTests : ResourceHooksTestBase + { + public BeforeDeleteTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + [Fact] + public void BeforeDelete() + { + // arrange + var discovery = SetDiscoverableHooks(); + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(discovery); + + var todoInput = new List() { new TodoItem() }; + + // act + hookExecutor.BeforeDelete(todoInput, It.IsAny()); + + // assert + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(todoInput, It.IsAny()), Times.Once()); + resourceDefinitionMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + resourceDefinitionMock.VerifyNoOtherCalls(); + } + + [Fact] + public void BeforeDelete_Without_Any_Hook_Implemented() + { + // arrange + var discovery = SetDiscoverableHooks(new ResourceHook[0]); + (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjects(discovery); + + // act + hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); + + // assert + resourceDefinitionMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + resourceDefinitionMock.VerifyNoOtherCalls(); + } + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeReadTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeReadTests.cs new file mode 100644 index 0000000000..2d2b17e184 --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeReadTests.cs @@ -0,0 +1,57 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class BeforeReadTests : ResourceHooksTestBase + { + public BeforeReadTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + + + [Fact] + public void BeforeRead() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeRead(It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + } + + + + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeUpdateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeUpdateTests.cs new file mode 100644 index 0000000000..67389fe3fe --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/BeforeUpdateTests.cs @@ -0,0 +1,136 @@ + +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Resources; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + + +namespace UnitTests.ResourceHooks +{ + + public class BeforeUpdateTests : ResourceHooksTestBase + { + public BeforeUpdateTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource() + .AddResource() + .Build(); + } + + + + + + [Fact] + public void BeforeUpdate() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeUpdate(todoInput, It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void BeforeUpdate_Without_Parent_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + [Fact] + public void BeforeUpdate_Without_Child_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.Verify(rd => rd.BeforeUpdate(todoInput, It.IsAny()), Times.Once()); + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + [Fact] + public void BeforeUpdate_Without_Any_Hook_Implemented() + { + // arrange + var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var todoResourceMock, + var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); + var todoInput = new List() { new TodoItem + { + Owner = new Person() + } + }; + // act + hookExecutor.BeforeUpdate(todoInput, It.IsAny()); + // assert + todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + todoResourceMock.VerifyNoOtherCalls(); + ownerResourceMock.Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + ownerResourceMock.VerifyNoOtherCalls(); + } + + + + + + } +} + diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutorTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutorTests.cs deleted file mode 100644 index a041f4d90b..0000000000 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutorTests.cs +++ /dev/null @@ -1,444 +0,0 @@ - -using JsonApiDotNetCore.Builders; -using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Generics; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExample.Resources; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using Xunit; - - -namespace UnitTests.ResourceHooks -{ - - public class ResourceHookExectuorTests : ResourceHooksTestBase - { - public ResourceHookExectuorTests() - { - // Build() exposes the static ResourceGraphBuilder.Instance member, which - // is consumed by ResourceDefinition class. - new ResourceGraphBuilder() - .AddResource() - .AddResource() - .Build(); - } - - - [Fact] - public void BeforeCreate() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - - [Fact] - public void BeforeCreate_Without_Parent_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void BeforeCreate_Without_Child_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Never()); - } - [Fact] - public void BeforeCreate_Without_Any_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(todoInput, It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Never()); - } - - [Fact] - public void AfterCreate() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - - [Fact] - public void AfterCreate_Without_Parent_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - - [Fact] - public void AfterCreate_Without_Child_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Never()); - } - - [Fact] - public void AfterCreate_Without_Any_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterCreate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterCreate(todoInput, It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Never()); - } - - [Fact] - public void BeforeRead() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeRead(It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); - } - [Fact] - public void AfterRead() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterRead(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny() ), Times.Once()); - } - - [Fact] - public void AfterRead_Without_Parent_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterRead(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); - } - - - [Fact] - public void AfterRead_Without_Child_Before_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.AfterRead }); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterRead(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); - } - [Fact] - public void AfterRead_Without_Child_After_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.BeforeRead }); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterRead(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); - } - - [Fact] - public void AfterRead_Without_Any_Child_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterRead(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); - } - [Fact] - public void AfterRead_Without_Any_Hook_Implemented() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterRead(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); - } - - [Fact] - public void BeforeUpdate() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.BeforeUpdate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void AfterUpdate() - { - // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjectsForNestedCase(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; - // act - hookExecutor.AfterUpdate(todoInput, It.IsAny()); - // assert - todoResourceMock.Verify(rd => rd.AfterUpdate(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); - } - [Fact] - public void BeforeDelete() - { - // arrange - var discovery = SetDiscoverableHooks(); - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(discovery); - - // act - hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); - - // assert - resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); - } - - [Fact] - public void BeforeDelete_Without_Any_Hook_Implemented() - { - // arrange - var discovery = SetDiscoverableHooks(new ResourceHook[0]); - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(discovery); - - // act - hookExecutor.BeforeDelete(It.IsAny>(), It.IsAny()); - - // assert - resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Never()); - } - - - [Fact] - public void AfterDelete() - { - // arrange - var discovery = SetDiscoverableHooks(); - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(discovery); - // act - hookExecutor.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()); - // assert - resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Once()); - } - - [Fact] - public void AfterDelete_Without_Any_Hook_Implemented() - { - // arrange - var discovery = SetDiscoverableHooks(new ResourceHook[0]); - (var resourceDefinitionMock, var contextMock, var hookExecutor) = CreateTestObjectsForSimpleCase(discovery); - // act - hookExecutor.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()); - // assert - resourceDefinitionMock.Verify(rd => rd.AfterDelete(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never()); - } - - - } -} - diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs index c545185991..e62e2b9167 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs @@ -18,35 +18,43 @@ namespace UnitTests.ResourceHooks public class ResourceHooksTestBase { - protected (Mock>, Mock, IResourceHookExecutor) - CreateTestObjectsForSimpleCase(IImplementedResourceHooks discovery) - { + protected (Mock>, Mock, IResourceHookExecutor) + CreateTestObjects(IImplementedResourceHooks discovery = null) + where TMain : class, IIdentifiable + { // creates the resource definition mock and corresponding ImplementedHooks discovery instance - (var todoItemResource, var identifiableTodoItemResource) = CreateResourceDefinition(discovery); + (var mainResource, var identifiableMainResource) = CreateResourceDefinition(discovery); // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. (var context, var processorFactory) = CreateContextAndProcessorMocks(); // wiring up the mocked GenericProcessorFactory to return the correct resource definition - SetupProcessorFactoryForResourceDefinition(processorFactory, todoItemResource.Object); + SetupProcessorFactoryForResourceDefinition(processorFactory, mainResource.Object); var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); - var hookExecutor = new ResourceHookExecutor(context.Object, discovery, meta); - return (todoItemResource, context, hookExecutor); + var hookExecutor = new ResourceHookExecutor(context.Object, discovery, meta); + + mainResource + .Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())) + .Callback, ResourceAction>( (x, y) => { + var first = x.FirstOrDefault(); + first.StringId = "123"; + }) + .Verifiable(); + return (mainResource, context, hookExecutor); } protected (Mock context, IResourceHookExecutor, Mock>, Mock>) - CreateTestObjectsForNestedCase( + CreateTestObjects( IImplementedResourceHooks mainDiscovery = null, - IImplementedResourceHooks nestedDiscovery = null, - List> orderOfExecution = null + IImplementedResourceHooks nestedDiscovery = null ) where TMain : class, IIdentifiable where TNested : class, IIdentifiable { // creates the resource definition mock and corresponding for a given set of discoverable hooks - (var mainResource, var identifiableMainResource) = CreateResourceDefinition(mainDiscovery, orderOfExecution); - var identifiableNestedResource = CreateResourceDefinition(nestedDiscovery, orderOfExecution).Item2; + (var mainResource, var identifiableMainResource) = CreateResourceDefinition(mainDiscovery); + var identifiableNestedResource = CreateResourceDefinition(nestedDiscovery).Item2; // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. (var context, var processorFactory) = CreateContextAndProcessorMocks(); @@ -60,6 +68,34 @@ public class ResourceHooksTestBase return (context, hookExecutor, mainResource, identifiableNestedResource); } + protected (Mock context, IResourceHookExecutor, Mock>, Mock>, Mock>) + CreateTestObjects( + IImplementedResourceHooks mainDiscovery = null, + IImplementedResourceHooks firstNestedDiscovery = null, + IImplementedResourceHooks secondNestedDiscovery = null + ) + where TMain : class, IIdentifiable + where TFirstNested : class, IIdentifiable + where TSecondNested : class, IIdentifiable + { + // creates the resource definition mock and corresponding for a given set of discoverable hooks + (var mainResource, var identifiableMainResource) = CreateResourceDefinition(mainDiscovery); + var identifiableFirstNestedResource = CreateResourceDefinition(firstNestedDiscovery).Item2; + var identifiableSecondNestedResource = CreateResourceDefinition(secondNestedDiscovery).Item2; + + // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + (var context, var processorFactory) = CreateContextAndProcessorMocks(); + + SetupProcessorFactoryForResourceDefinition(processorFactory, mainResource.Object); + var meta = new ResourceHookMetaInfo(context.Object.GenericProcessorFactory, ResourceGraph.Instance); + var hookExecutor = new ResourceHookExecutor(context.Object, mainDiscovery, meta); + + SetupProcessorFactoryForResourceDefinition(processorFactory, identifiableFirstNestedResource.Object); + SetupProcessorFactoryForResourceDefinition(processorFactory, identifiableSecondNestedResource.Object); + + return (context, hookExecutor, mainResource, identifiableFirstNestedResource, identifiableSecondNestedResource); + } + protected IImplementedResourceHooks SetDiscoverableHooks(ResourceHook[] implementedHooks = null) where TEntity : class, IIdentifiable @@ -120,18 +156,19 @@ IImplementedResourceHooks discovery resourceDefinition .As>() .Setup(rd => rd.ShouldExecuteHook(It.IsAny())) - .Returns((hook) => discovery.ImplementedHooks.Contains(hook)); + .Returns((hook) => discovery.ImplementedHooks.Contains(hook)) + .Verifiable(); resourceDefinition .As>() .Setup(rd => rd.ShouldExecuteHook(It.IsAny())) - .Returns((hook) => discovery.ImplementedHooks.Contains(hook)); + .Returns((hook) => discovery.ImplementedHooks.Contains(hook)) + .Verifiable(); return resourceDefinition; } private (Mock>, Mock>) CreateResourceDefinition - (IImplementedResourceHooks discovery, - List> orderOfExecution = null + (IImplementedResourceHooks discovery ) where TModel : class, IIdentifiable { diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index 058f317f06..f52faf9bb9 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -21,5 +21,6 @@ + From 906585e75dbdf618398c61a4ce97078b5fe79131 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 17 Apr 2019 17:29:54 +0200 Subject: [PATCH 026/168] feat: many-to-many support with identifiable jointable entity --- .../Data/AppDbContext.cs | 4 + .../Models/Article.cs | 6 + .../Models/ArticleTag.cs | 16 +- .../JsonApiDotNetCoreExample/Models/Author.cs | 1 + .../Services/ResourceHookExecutor.cs | 49 ++-- .../Services/ResourceHookMetaInfo.cs | 135 +++++++-- .../ResourceHookExecutor/AfterReadTests.cs | 264 ++++++++++++------ .../ResourceHookExecutor/AfterUpdateTests.cs | 1 + .../ResourceHooks/ResourceHooksTestBase.cs | 2 +- test/UnitTests/UnitTests.csproj | 1 + 10 files changed, 340 insertions(+), 139 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 5486eedb6f..2c8e561d29 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -40,6 +40,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .HasKey(bc => new { bc.ArticleId, bc.TagId }); + + modelBuilder.Entity() + .HasKey(bc => new { bc.ArticleId, bc.TagId }); } public DbSet TodoItems { get; set; } @@ -57,6 +60,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) public DbSet Students { get; set; } public DbSet PersonRoles { get; set; } public DbSet ArticleTags { get; set; } + public DbSet IdentifiableArticleTags { get; set; } public DbSet Tags { get; set; } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs index 8d4d310f70..6b4648e8d2 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs @@ -17,5 +17,11 @@ public class Article : Identifiable [HasManyThrough(nameof(ArticleTags))] public List Tags { get; set; } public List ArticleTags { get; set; } + + + [NotMapped] + [HasManyThrough(nameof(IdentifiableArticleTags))] + public List IdentifiableTags { get; set; } + public List IdentifiableArticleTags { get; set; } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/ArticleTag.cs b/src/Examples/JsonApiDotNetCoreExample/Models/ArticleTag.cs index 2e45ea84ae..8b180cc203 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/ArticleTag.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/ArticleTag.cs @@ -2,7 +2,7 @@ namespace JsonApiDotNetCoreExample.Models { - public class ArticleTag : Identifiable + public class ArticleTag { public int ArticleId { get; set; } public Article Article { get; set; } @@ -10,4 +10,18 @@ public class ArticleTag : Identifiable public int TagId { get; set; } public Tag Tag { get; set; } } + + + public class IdentifiableArticleTag : Identifiable + { + public int ArticleId { get; set; } + [HasOne("article")] + public Article Article { get; set; } + + public int TagId { get; set; } + [HasOne("Tag")] + public Tag Tag { get; set; } + + public string SomeMetaData { get; set; } + } } \ No newline at end of file diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs index c77ad007c8..246118a53b 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs @@ -12,3 +12,4 @@ public class Author : Identifiable public List
Articles { get; set; } } } + diff --git a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs index e685196df5..4f2e74d493 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookExecutor.cs @@ -239,7 +239,7 @@ Func, IEnumerable, IEnumera var nextLayer = relationshipsInCurrentLayer.Values.SelectMany(entities => entities); if (nextLayer.Any()) { - var uniqueTypesInNextLayer = relationshipsInCurrentLayer.Keys.Select(k => k.Type); + var uniqueTypesInNextLayer = relationshipsInCurrentLayer.Keys.Select(k => k.TargetType); _meta.UpdateMetaInformation(uniqueTypesInNextLayer); BreadthFirstTraverse(nextLayer, hookExecutionAction); } @@ -253,34 +253,35 @@ Func, IEnumerable, IEnumera ///
/// Hook targets for current layer. /// Current layer. - Dictionary> ExtractionLoop( + Dictionary> ExtractionLoop( IEnumerable currentLayer ) { - var relationshipsInCurrentLayer = new Dictionary>(); + var relationshipsInCurrentLayer = new Dictionary>(); foreach (IIdentifiable currentLayerEntity in currentLayer) { - foreach (RelationshipAttribute attr in _meta.GetMetaEntries(currentLayerEntity)) + foreach (RelationshipProxy proxy in _meta.GetMetaEntries(currentLayerEntity)) { - var relationshipValue = attr.GetValue(currentLayerEntity); + + var relationshipValue = proxy.GetValue(currentLayerEntity); // skip iteration if there is no relation assigned @TODO what about to-many: will we have empty lists or null? and how does this relate to that being Included by query params if (relationshipValue == null) continue; - if (!(relationshipValue is IEnumerable)) + if (!(relationshipValue is IEnumerable relatedEntities)) { // 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(relationshipValue.GetType()); list.Add(relationshipValue); - relationshipValue = list; + relatedEntities = (IEnumerable)list; } - var relatedEntities = relationshipValue as IEnumerable; - if (!relationshipsInCurrentLayer.ContainsKey(attr)) + + if (!relationshipsInCurrentLayer.ContainsKey(proxy)) { - relationshipsInCurrentLayer[attr] = relatedEntities; + relationshipsInCurrentLayer[proxy] = relatedEntities; } else { - relationshipsInCurrentLayer[attr].Concat(relatedEntities); + relationshipsInCurrentLayer[proxy] = relationshipsInCurrentLayer[proxy].Concat(relatedEntities); } } @@ -288,25 +289,26 @@ IEnumerable currentLayer return relationshipsInCurrentLayer; } + /// /// Executes the hooks for every key in relationshipsInCurrentLayer, /// /// Hook targets for current layer. /// Hook execution. void ExecutionLoop( - Dictionary> relationshipsInCurrentLayer, + Dictionary> relationshipsInCurrentLayer, Func, IEnumerable, IEnumerable> hookExecution ) { var relationships = relationshipsInCurrentLayer.Keys.ToArray(); - foreach (var attr in relationships) + foreach (var proxy in relationships) { - var entities = relationshipsInCurrentLayer[attr]; + var entities = relationshipsInCurrentLayer[proxy]; var uniqueEntities = new HashSet(entities); - var innerHookContainer = _meta.GetResourceHookContainer(attr.Type); + var innerHookContainer = _meta.GetResourceHookContainer(proxy.TargetType); var filteredUniqueEntites = hookExecution(innerHookContainer, uniqueEntities); - relationshipsInCurrentLayer[attr] = filteredUniqueEntites.ToArray(); + relationshipsInCurrentLayer[proxy] = filteredUniqueEntites.ToArray(); } } @@ -320,7 +322,7 @@ Func, IEnumerable, IEnumera /// Hook targets for current layer. void AssignmentLoop( IEnumerable currentLayer, - Dictionary> relationshipsInCurrentLayer + Dictionary> relationshipsInCurrentLayer ) { // @TODO IM NOT EVEN SURE IF WE NEED TO REASSIGN ?! @@ -328,25 +330,26 @@ Dictionary> relationshipsInCur // to perform filter check foreach (IIdentifiable currentLayerEntity in currentLayer) { - foreach (RelationshipAttribute attr in _meta.GetMetaEntries(currentLayerEntity)) + foreach (RelationshipProxy proxy in _meta.GetMetaEntries(currentLayerEntity)) { + // skip the iteration there is nothing to a given relationship - if (!relationshipsInCurrentLayer.TryGetValue(attr, out var parsedEntities)) + if (!relationshipsInCurrentLayer.TryGetValue(proxy, out var parsedEntities)) { continue; } - var relationshipValue = attr.GetValue(currentLayerEntity); + var relationshipValue = proxy.GetValue(currentLayerEntity); if (relationshipValue is IEnumerable relationshipCollection) { - relationshipValue = relationshipCollection.Intersect(parsedEntities); - attr.SetValue(currentLayerEntity, relationshipValue); + relationshipCollection = (relationshipCollection.Intersect(parsedEntities)); + proxy.SetValue(currentLayerEntity, relationshipCollection); } else if (relationshipValue is IIdentifiable relationshipSingle) { if (!parsedEntities.Contains(relationshipValue)) { - attr.SetValue(currentLayerEntity, null); + proxy.SetValue(currentLayerEntity, null); } } } diff --git a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs index 1b94d04136..91ba436c68 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs @@ -1,19 +1,55 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Services { + public class RelationshipProxy + { + readonly bool _isHasManyThrough; + public RelationshipAttribute Attribute { get; set; } + public RelationshipProxy(RelationshipAttribute attr, Type targetType) + { + TargetType = targetType; + Attribute = attr; + _isHasManyThrough |= (attr is HasManyThroughAttribute throughAttr && TargetType == throughAttr.ThroughType); + } + + public object GetValue(IIdentifiable entity) + { + if (_isHasManyThrough) + { + return ((HasManyThroughAttribute)Attribute).ThroughProperty.GetValue(entity); + } + return Attribute.GetValue(entity); + } + + public void SetValue(IIdentifiable entity, object value) + { + if (_isHasManyThrough) + { + var list = (IEnumerable)value; + ((HasManyThroughAttribute)Attribute).ThroughProperty.SetValue(entity, TypeHelper.ConvertCollection(list, TargetType )); + return; + } + Attribute.SetValue(entity, value); + } + + public Type TargetType { get; private set; } + } + public interface IResourceHookMetaInfo { - IEnumerable GetMetaEntries(IIdentifiable currentLayerEntity); + IEnumerable GetMetaEntries(IIdentifiable currentLayerEntity); IResourceHookContainer GetResourceHookContainer(Type targetEntity, ResourceHook hook = ResourceHook.None); IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable; - Dictionary UpdateMetaInformation(IEnumerable nextLayerTypes, ResourceHook hook = ResourceHook.None); - Dictionary UpdateMetaInformation(IEnumerable nextLayerTypes, IEnumerable hooks); + Dictionary UpdateMetaInformation(IEnumerable nextLayerTypes, ResourceHook hook = ResourceHook.None); + Dictionary UpdateMetaInformation(IEnumerable nextLayerTypes, IEnumerable hooks); + Type GetTypeFromRelationshipAttribute(RelationshipAttribute attr); } public class ResourceHookMetaInfo : IResourceHookMetaInfo @@ -23,7 +59,7 @@ public class ResourceHookMetaInfo : IResourceHookMetaInfo protected readonly IResourceGraph _graph; protected readonly Dictionary> _hookContainers; protected readonly List _targetedHooksForRelatedEntities; - protected Dictionary _meta; + protected Dictionary _meta; public ResourceHookMetaInfo( IGenericProcessorFactory genericProcessorFactory, @@ -32,25 +68,26 @@ IResourceGraph graph { _genericProcessorFactory = genericProcessorFactory; _graph = graph; - _meta = new Dictionary(); + _meta = new Dictionary(); _hookContainers = new Dictionary>(); _targetedHooksForRelatedEntities = new List(); } - public IEnumerable GetMetaEntries(IIdentifiable currentLayerEntity) + public IEnumerable GetMetaEntries(IIdentifiable currentLayerEntity) { foreach (string metaKey in _meta.Keys) { - var attribute = _meta[metaKey]; + var proxy = _meta[metaKey]; /// because currentLayer is not type-homogeneous (which is /// why we need to use IIdentifiable for the list type of /// that layer), we need to check if relatedType is really /// related to parentType. We do this through comparison of Metakey - string requiredMetaKey = CreateMetaKey(attribute, currentLayerEntity.GetType()); + string requiredMetaKey = CreateMetaKey(proxy.Attribute, currentLayerEntity.GetType()); if (metaKey != requiredMetaKey) continue; - yield return attribute; + + yield return proxy; } } @@ -129,7 +166,7 @@ public IResourceHookContainer GetResourceHookContainer(Resourc /// The meta dict. /// Unique list of types to extract metadata from /// The target resource hook types - public Dictionary + public Dictionary UpdateMetaInformation( IEnumerable nextLayerTypes, IEnumerable hooks) @@ -153,7 +190,7 @@ public Dictionary var contextEntity = _graph.GetContextEntity(targetType); var relationshipsForContextEntity = contextEntity.Relationships.ToDictionary( attr => CreateMetaKey(attr, targetType, checkForDuplicates: true), - attr => attr); + attr => new RelationshipProxy(attr, GetTypeFromRelationshipAttribute(attr))); /// keep only the meta info we really need for the traversal of the next layer /// also remove duplicates. PruneMetaDictionary(relationshipsForContextEntity, targetHook); @@ -187,7 +224,7 @@ void CheckForTargetHookExistence() /// The meta dict. /// Unique list of types to extract metadata from /// The target resource hook type - public Dictionary + public Dictionary UpdateMetaInformation( IEnumerable nextLayerTypes, ResourceHook hook = ResourceHook.None) @@ -208,8 +245,31 @@ public Dictionary /// Parent type. string CreateMetaKey(RelationshipAttribute attr, Type parentType, bool checkForDuplicates = false) { - var relationType = attr.IsHasOne ? "has-one" : "has-many"; - string newKey = $"{parentType.Name} {relationType} {attr.RelationshipPath}"; + string relationType; + string rightHandIdentifier; + if (attr is HasManyThroughAttribute manyThroughAttr) + { + relationType = "has-many-through"; + rightHandIdentifier = manyThroughAttr.ThroughProperty.Name; + } else if (attr is HasManyAttribute manyAttr) + { + relationType = "has-many"; + rightHandIdentifier = manyAttr.RelationshipPath; + } + else + { + relationType = "has-one"; + rightHandIdentifier = attr.RelationshipPath; + } + string newKey = $"{parentType.Name} {relationType} {rightHandIdentifier}"; + + + var forbiddenInverse = _meta.Where(pair => pair.Key.Contains("has-many-through")).Select(pair => InverseKey(pair.Key)).ToArray(); + if (checkForDuplicates && forbiddenInverse.Contains(newKey)) + { + return $"INVERSE-MTM-{Guid.NewGuid()}"; + } + if (checkForDuplicates && _meta.ContainsKey(newKey)) { return $"DUPLICATE-{Guid.NewGuid()}"; @@ -221,27 +281,56 @@ string CreateMetaKey(RelationshipAttribute attr, Type parentType, bool checkForD /// Gets rid of keys in the meta dict that won't be needed for the next layer. /// /// It does so by: - /// 1) checking if there was at all a IResourceHookExecutor - /// implemented for this type (ResourceDefinition by default); - /// 2) then checking if there is a implementation of the particular - /// target hook. + /// + /// 1) checking if there was at all a IResourceHookExecutor implemented for this type (ResourceDefinition by default); + /// 2) then checking if there is a implementation of the particular target hook. + /// 3) lastly, in the case of many-to-many, we need to make sure we don't include a meta entry that navigates back from the right side + /// to the left side, or it will get stuck bouncing back and forth and blow up with a stack overflow. /// void PruneMetaDictionary( - Dictionary meta, + Dictionary meta, ResourceHook targetHook) { - var dupes = meta.Where(pair => pair.Key.Contains("DUPLICATE")).Select(pair => pair.Key).ToArray(); - foreach (string target in dupes) + + + + var inverseKeys = meta.Where(pair => pair.Key.Contains("INVERSE")).Select(pair => pair.Key).ToArray(); + foreach (string target in inverseKeys) + { + meta.Remove(target); + } + var dupesKeys = meta.Where(pair => pair.Key.Contains("DUPLICATE")).Select(pair => pair.Key).ToArray(); + foreach (string target in dupesKeys) { meta.Remove(target); } - var noHookImplementation = meta.Where(pair => GetResourceHookContainer(pair.Value.Type, targetHook) == null).Select(pair => pair.Key).ToArray(); - foreach (string target in noHookImplementation) + var noHookImplementationKeys = meta.Where(pair => GetResourceHookContainer(pair.Value.TargetType, targetHook) == null).Select(pair => pair.Key).ToArray(); + foreach (string target in noHookImplementationKeys) { meta.Remove(target); } + + } + string InverseKey(string key) + { + var splitted = key.Split(new string[] { " has-many-through " }, StringSplitOptions.None).Reverse().ToArray(); + splitted[0] = splitted[0].Remove(splitted[0].Length - 1); + return string.Join(" has-one ", splitted); + } + public Type GetTypeFromRelationshipAttribute(RelationshipAttribute attr) + { + if (attr is HasManyThroughAttribute throughAttr) + { + if (typeof(IIdentifiable).IsAssignableFrom(throughAttr.ThroughType)) + { + return throughAttr.ThroughType; + } + return attr.Type; + } + return attr.Type; + } } } \ No newline at end of file diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs index 745e70efbc..ef3acde2e5 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs @@ -11,7 +11,8 @@ using System.Collections.Generic; using System.Linq; using Xunit; - +using Bogus; +using System.Collections; namespace UnitTests.ResourceHooks { @@ -23,139 +24,220 @@ public AfterReadTests() // Build() exposes the static ResourceGraphBuilder.Instance member, which // is consumed by ResourceDefinition class. new ResourceGraphBuilder() - .AddResource() - .AddResource() + .AddResource
() + .AddResource() + .AddResource() .Build(); } + (List
, List, List) CreateDummyData() + { + var tagsSubset = new Faker().Generate(3).ToList(); + var joinsSubSet = new Faker().Generate(3).ToList(); + var articleTagsSubset = new Article() { IdentifiableArticleTags = joinsSubSet }; + for (int i = 0; i < 3; i++) + { + joinsSubSet[i].Article = articleTagsSubset; + joinsSubSet[i].Tag = tagsSubset[i]; + } + + var allTags = new Faker().Generate(3).ToList().Concat(tagsSubset).ToList(); + var completeJoin = new Faker().Generate(6).ToList(); + + var articleWithAllTags = new Article() { IdentifiableArticleTags = completeJoin }; + + for (int i = 0; i < 6; i++) + { + completeJoin[i].Article = articleWithAllTags; + completeJoin[i].Tag = allTags[i]; + } + + var allJoins = joinsSubSet.Concat(completeJoin).ToList(); + + var articles = new List
() { articleTagsSubset, articleWithAllTags }; + return (articles, allJoins, allTags); + } + + + [Fact] public void AfterRead() { // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; + var articleDiscovery = SetDiscoverableHooks
(); + var joinDiscovery = SetDiscoverableHooks(); + var tagDiscovery = SetDiscoverableHooks(); + + + + (var contextMock, var hookExecutor, var articleResourceMock, + var joinResourceMock, var tagResourceMock ) = CreateTestObjects(articleDiscovery, joinDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + // act - hookExecutor.AfterRead(todoInput, It.IsAny()); + hookExecutor.AfterRead(articles, It.IsAny()); + // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + articleResourceMock.Verify(rd => rd.AfterRead(articles, It.IsAny()), Times.Once()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + joinResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), null), Times.Once()); + joinResourceMock.Verify(rd => rd.AfterRead(It.Is>( (collection) => !collection.Except(joins).Any()), It.IsAny()), Times.Once()); + joinResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + joinResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), null), Times.Once()); + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>( (collection) => !collection.Except(tags).Any()), It.IsAny()), Times.Once()); + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); } + + [Fact] public void AfterRead_Without_Parent_Hook_Implemented() { // arrange - var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - var personDiscovery = SetDiscoverableHooks(); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; + var articleDiscovery = SetDiscoverableHooks
(new ResourceHook[0]); + var joinDiscovery = SetDiscoverableHooks(); + var tagDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var articleResourceMock, + var joinResourceMock, var tagResourceMock) = CreateTestObjects(articleDiscovery, joinDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + // act - hookExecutor.AfterRead(todoInput, It.IsAny()); + hookExecutor.AfterRead(articles, It.IsAny()); + // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + joinResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), null), Times.Once()); + joinResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(joins).Any()), It.IsAny()), Times.Once()); + joinResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + joinResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), null), Times.Once()); + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), It.IsAny()), Times.Once()); + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); } - [Fact] - public void AfterRead_Without_Child_Before_Hook_Implemented() + public void AfterRead_Without_Children_Before_Hooks_Implemented() { // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.AfterRead }); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; + var articleDiscovery = SetDiscoverableHooks
(); + var joinDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.AfterRead }); + var tagDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.AfterRead }); + + (var contextMock, var hookExecutor, var articleResourceMock, + var joinResourceMock, var tagResourceMock) = CreateTestObjects(articleDiscovery, joinDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + // act - hookExecutor.AfterRead(todoInput, It.IsAny()); + hookExecutor.AfterRead(articles, It.IsAny()); + // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); + articleResourceMock.Verify(rd => rd.AfterRead(articles, It.IsAny()), Times.Once()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + joinResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(joins).Any()), It.IsAny()), Times.Once()); + joinResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + joinResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), It.IsAny()), Times.Once()); + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); } + [Fact] - public void AfterRead_Without_Child_After_Hook_Implemented() + public void AfterRead_Without_Children_After_Hooks_Implemented() { // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.BeforeRead }); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; + var articleDiscovery = SetDiscoverableHooks
(); + var joinDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.BeforeRead }); + var tagDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.BeforeRead }); + + (var contextMock, var hookExecutor, var articleResourceMock, + var joinResourceMock, var tagResourceMock) = CreateTestObjects(articleDiscovery, joinDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + // act - hookExecutor.AfterRead(todoInput, It.IsAny()); + hookExecutor.AfterRead(articles, It.IsAny()); + // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Once()); + articleResourceMock.Verify(rd => rd.AfterRead(articles, It.IsAny()), Times.Once()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + joinResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), null), Times.Once()); + joinResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + joinResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), null), Times.Once()); + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); } [Fact] - public void AfterRead_Without_Any_Child_Hook_Implemented() + public void AfterRead_Without_Any_Children_Hooks_Implemented() { // arrange - var todoDiscovery = SetDiscoverableHooks(); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; + var articleDiscovery = SetDiscoverableHooks
(); + var joinDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var tagDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var articleResourceMock, + var joinResourceMock, var tagResourceMock) = CreateTestObjects(articleDiscovery, joinDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + // act - hookExecutor.AfterRead(todoInput, It.IsAny()); + hookExecutor.AfterRead(articles, It.IsAny()); + // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); + articleResourceMock.Verify(rd => rd.AfterRead(articles, It.IsAny()), Times.Once()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + joinResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + joinResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); } [Fact] public void AfterRead_Without_Any_Hook_Implemented() { // arrange - var todoDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - var personDiscovery = SetDiscoverableHooks(new ResourceHook[0]); - - (var contextMock, var hookExecutor, var todoResourceMock, - var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery); - var todoInput = new List() { new TodoItem - { - Owner = new Person() - } - }; + var articleDiscovery = SetDiscoverableHooks
(new ResourceHook[0]); + var joinDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + var tagDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var articleResourceMock, + var joinResourceMock, var tagResourceMock) = CreateTestObjects(articleDiscovery, joinDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + // act - hookExecutor.AfterRead(todoInput, It.IsAny()); + hookExecutor.AfterRead(articles, It.IsAny()); + // assert - todoResourceMock.Verify(rd => rd.AfterRead(todoInput, It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), It.IsAny()), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), It.IsAny()), Times.Never()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + joinResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + joinResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); } } } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterUpdateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterUpdateTests.cs index cdc13b1375..4d2a8f981b 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterUpdateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterUpdateTests.cs @@ -44,6 +44,7 @@ public void AfterUpdate() // act hookExecutor.AfterUpdate(todoInput, It.IsAny()); // assert + todoResourceMock.Verify(rd => rd.AfterUpdate(todoInput, It.IsAny()), Times.Once()); ownerResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), It.IsAny()), Times.Once()); todoResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs index e62e2b9167..1504de20cd 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestBase.cs @@ -91,7 +91,7 @@ public class ResourceHooksTestBase var hookExecutor = new ResourceHookExecutor(context.Object, mainDiscovery, meta); SetupProcessorFactoryForResourceDefinition(processorFactory, identifiableFirstNestedResource.Object); - SetupProcessorFactoryForResourceDefinition(processorFactory, identifiableSecondNestedResource.Object); + SetupProcessorFactoryForResourceDefinition(processorFactory, identifiableSecondNestedResource.Object); return (context, hookExecutor, mainResource, identifiableFirstNestedResource, identifiableSecondNestedResource); } diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index f52faf9bb9..f8ddc02818 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -13,6 +13,7 @@ + From 94608fac879216d0720663fbea284531e9ca1e9e Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 17 Apr 2019 18:19:20 +0200 Subject: [PATCH 027/168] feat: many-to-many support with non-identifiable jointable entities --- .../Services/ResourceHookMetaInfo.cs | 59 ++++- ... IdentifiableManyToMany_AfterReadTests.cs} | 4 +- .../ManyToMany_AfterReadTests .cs | 209 ++++++++++++++++++ 3 files changed, 265 insertions(+), 7 deletions(-) rename test/UnitTests/ResourceHooks/ResourceHookExecutor/{AfterReadTests.cs => IdentifiableManyToMany_AfterReadTests.cs} (98%) create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/ManyToMany_AfterReadTests .cs diff --git a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs index 91ba436c68..0968fb5fc7 100644 --- a/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs +++ b/src/JsonApiDotNetCore/Services/ResourceHookMetaInfo.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -11,19 +12,44 @@ namespace JsonApiDotNetCore.Services public class RelationshipProxy { readonly bool _isHasManyThrough; + readonly bool _skipJoinTable; public RelationshipAttribute Attribute { get; set; } public RelationshipProxy(RelationshipAttribute attr, Type targetType) { TargetType = targetType; Attribute = attr; - _isHasManyThrough |= (attr is HasManyThroughAttribute throughAttr && TargetType == throughAttr.ThroughType); + if (attr is HasManyThroughAttribute throughAttr) + { + _isHasManyThrough = true; + if (TargetType != throughAttr.ThroughType) + { + _skipJoinTable = true; + } + } } public object GetValue(IIdentifiable entity) { if (_isHasManyThrough) { - return ((HasManyThroughAttribute)Attribute).ThroughProperty.GetValue(entity); + var throughAttr = (HasManyThroughAttribute)Attribute; + if (!_skipJoinTable) + { + return throughAttr.ThroughProperty.GetValue(entity); + } + else + { + var collection = new List(); + var joinEntities = (IList)throughAttr.ThroughProperty.GetValue(entity); + 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); } @@ -32,9 +58,32 @@ public void SetValue(IIdentifiable entity, object value) { if (_isHasManyThrough) { - var list = (IEnumerable)value; - ((HasManyThroughAttribute)Attribute).ThroughProperty.SetValue(entity, TypeHelper.ConvertCollection(list, TargetType )); - return; + if (!_skipJoinTable) + { + var list = (IEnumerable)value; + ((HasManyThroughAttribute)Attribute).ThroughProperty.SetValue(entity, TypeHelper.ConvertCollection(list, TargetType)); + return; + } + else + { + var throughAttr = (HasManyThroughAttribute)Attribute; + var joinEntities = (IEnumerable)throughAttr.ThroughProperty.GetValue(entity); + + var filteredList = new List(); + var rightEntities = TypeHelper.ConvertCollection((IEnumerable)value, TargetType); + foreach (var je in joinEntities) + { + + if (rightEntities.Contains(throughAttr.RightProperty.GetValue(je))) + { + filteredList.Add(je); + } + } + + throughAttr.ThroughProperty.SetValue(entity, TypeHelper.ConvertCollection(filteredList, throughAttr.ThroughType)); + return; + } + } Attribute.SetValue(entity, value); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/IdentifiableManyToMany_AfterReadTests.cs similarity index 98% rename from test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs rename to test/UnitTests/ResourceHooks/ResourceHookExecutor/IdentifiableManyToMany_AfterReadTests.cs index ef3acde2e5..e64555548c 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/AfterReadTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/IdentifiableManyToMany_AfterReadTests.cs @@ -17,9 +17,9 @@ namespace UnitTests.ResourceHooks { - public class AfterReadTests : ResourceHooksTestBase + public class IdentifiableJoinTable_AfterReadTests : ResourceHooksTestBase { - public AfterReadTests() + public IdentifiableJoinTable_AfterReadTests() { // Build() exposes the static ResourceGraphBuilder.Instance member, which // is consumed by ResourceDefinition class. diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/ManyToMany_AfterReadTests .cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/ManyToMany_AfterReadTests .cs new file mode 100644 index 0000000000..5171985788 --- /dev/null +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/ManyToMany_AfterReadTests .cs @@ -0,0 +1,209 @@ +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using Moq; +using System.Collections.Generic; +using System.Linq; +using Xunit; +using Bogus; + +namespace UnitTests.ResourceHooks +{ + + public class ManyToMany_AfterReadTests : ResourceHooksTestBase + { + public ManyToMany_AfterReadTests() + { + // Build() exposes the static ResourceGraphBuilder.Instance member, which + // is consumed by ResourceDefinition class. + new ResourceGraphBuilder() + .AddResource
() + .AddResource() + .Build(); + } + + (List
, List, List) CreateDummyData() + { + var tagsSubset = new Faker().Generate(3).ToList(); + var joinsSubSet = new Faker().Generate(3).ToList(); + var articleTagsSubset = new Article() { ArticleTags = joinsSubSet }; + for (int i = 0; i < 3; i++) + { + joinsSubSet[i].Article = articleTagsSubset; + joinsSubSet[i].Tag = tagsSubset[i]; + } + + var allTags = new Faker().Generate(3).ToList().Concat(tagsSubset).ToList(); + var completeJoin = new Faker().Generate(6).ToList(); + + var articleWithAllTags = new Article() { ArticleTags = completeJoin }; + + for (int i = 0; i < 6; i++) + { + completeJoin[i].Article = articleWithAllTags; + completeJoin[i].Tag = allTags[i]; + } + + var allJoins = joinsSubSet.Concat(completeJoin).ToList(); + + var articles = new List
() { articleTagsSubset, articleWithAllTags }; + return (articles, allJoins, allTags); + } + + + [Fact] + public void AfterRead() + { + // arrange + var articleDiscovery = SetDiscoverableHooks
(); + var tagDiscovery = SetDiscoverableHooks(); + + + + (var contextMock, var hookExecutor, var articleResourceMock, + var tagResourceMock ) = CreateTestObjects(articleDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + + // act + hookExecutor.AfterRead(articles, It.IsAny()); + + // assert + articleResourceMock.Verify(rd => rd.AfterRead(articles, It.IsAny()), Times.Once()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), null), Times.Once()); + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>( (collection) => !collection.Except(tags).Any()), It.IsAny()), Times.Once()); + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); + } + + + + [Fact] + public void AfterRead_Without_Parent_Hook_Implemented() + { + // arrange + var articleDiscovery = SetDiscoverableHooks
(new ResourceHook[0]); + var tagDiscovery = SetDiscoverableHooks(); + + (var contextMock, var hookExecutor, var articleResourceMock, + var tagResourceMock) = CreateTestObjects(articleDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + + // act + hookExecutor.AfterRead(articles, It.IsAny()); + + // assert + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), null), Times.Once()); + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), It.IsAny()), Times.Once()); + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void AfterRead_Without_Children_Before_Hooks_Implemented() + { + // arrange + var articleDiscovery = SetDiscoverableHooks
(); + var tagDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.AfterRead }); + + (var contextMock, var hookExecutor, var articleResourceMock, + var tagResourceMock) = CreateTestObjects(articleDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + + // act + hookExecutor.AfterRead(articles, It.IsAny()); + + // assert + articleResourceMock.Verify(rd => rd.AfterRead(articles, It.IsAny()), Times.Once()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), It.IsAny()), Times.Once()); + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void AfterRead_Without_Children_After_Hooks_Implemented() + { + // arrange + var articleDiscovery = SetDiscoverableHooks
(); + var tagDiscovery = SetDiscoverableHooks(new ResourceHook[] { ResourceHook.BeforeRead }); + + (var contextMock, var hookExecutor, var articleResourceMock, + var tagResourceMock) = CreateTestObjects(articleDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + + // act + hookExecutor.AfterRead(articles, It.IsAny()); + + // assert + articleResourceMock.Verify(rd => rd.AfterRead(articles, It.IsAny()), Times.Once()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.Verify(rd => rd.BeforeRead(It.IsAny(), null), Times.Once()); + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); + } + + [Fact] + public void AfterRead_Without_Any_Children_Hooks_Implemented() + { + // arrange + var articleDiscovery = SetDiscoverableHooks
(); + var tagDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var articleResourceMock, + var tagResourceMock) = CreateTestObjects(articleDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + + // act + hookExecutor.AfterRead(articles, It.IsAny()); + + // assert + articleResourceMock.Verify(rd => rd.AfterRead(articles, It.IsAny()), Times.Once()); + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); + } + [Fact] + public void AfterRead_Without_Any_Hook_Implemented() + { + // arrange + var articleDiscovery = SetDiscoverableHooks
(new ResourceHook[0]); + var tagDiscovery = SetDiscoverableHooks(new ResourceHook[0]); + + (var contextMock, var hookExecutor, var articleResourceMock, + var tagResourceMock) = CreateTestObjects(articleDiscovery, tagDiscovery); + + (var articles, var joins, var tags) = CreateDummyData(); + + // act + hookExecutor.AfterRead(articles, It.IsAny()); + + // assert + articleResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + articleResourceMock.VerifyNoOtherCalls(); + + + tagResourceMock.As>().Verify(rd => rd.ShouldExecuteHook(It.IsAny()), Times.AtLeastOnce()); + tagResourceMock.VerifyNoOtherCalls(); + } + } +} + From f58c3c86e056f7443a3a5e4675dccd7923981ef6 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Fri, 19 Apr 2019 14:08:34 +0200 Subject: [PATCH 028/168] feat: added support for entities datasets with populated inverse navigationproperties --- JsonApiDotnetCore.sln | 1 + .../JsonApiDotNetCoreExample.csproj | 1 + .../Models/TodoItem.cs | 1 + .../JsonApiDotNetCore.csproj | 1 + .../Services/EntityResourceService.cs | 4 +- .../Services/ResourceHookExecutor.cs | 172 ++++++++---- .../Services/ResourceHookMetaInfo.cs | 245 ++++++++++-------- .../ResourceHookExecutor/AfterCreateTests.cs | 41 +-- .../ResourceHookExecutor/AfterUpdateTests.cs | 44 ++-- .../ResourceHookExecutor/BeforeCreateTests.cs | 44 ++-- .../ResourceHookExecutor/BeforeDeleteTests.cs | 9 +- .../ResourceHookExecutor/BeforeReadTests.cs | 9 +- .../ResourceHookExecutor/BeforeUpdateTests.cs | 42 ++- .../IdentifiableManyToMany_AfterReadTests.cs | 6 +- .../ResourceHookExecutor/ScenarioTests.cs | 134 ++++++++++ .../ResourceHooks/ResourceHooksTestBase.cs | 10 + 16 files changed, 475 insertions(+), 289 deletions(-) mode change 100755 => 100644 src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj create mode 100644 test/UnitTests/ResourceHooks/ResourceHookExecutor/ScenarioTests.cs diff --git a/JsonApiDotnetCore.sln b/JsonApiDotnetCore.sln index bb6af88968..729721080e 100644 --- a/JsonApiDotnetCore.sln +++ b/JsonApiDotnetCore.sln @@ -205,6 +205,7 @@ Global {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x86.ActiveCfg = Release|Any CPU {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x86.Build.0 = Release|Any CPU {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj old mode 100755 new mode 100644 index d56e91f21e..2a26c1f574 --- a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj +++ b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj @@ -26,6 +26,7 @@ + diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs index 7ae957f4a5..042179124f 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCoreExample.Models diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index 01a371e021..9440585b00 100644 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -30,6 +30,7 @@ + System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[JsonApiDotNetCore.Models.ResourceIdentifierObject]' to type 'System.Collections.Generic.List`1[System.Collections.Generic.Dictionary`2[System.String,System.Object]]'. - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.SetHasManyRelationship(Object entity, PropertyInfo[] entityProperties, RelationshipAttribute attr, ContextEntity contextEntity, Dictionary`2 relationships) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 216 - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.SetRelationships(Object entity, ContextEntity contextEntity, Dictionary`2 relationships) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 156 - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.DocumentToObject(DocumentData data) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 92 - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.Deserialize(String requestBody) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 33 - --- End of inner exception stack trace --- - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.Deserialize(String requestBody) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 38 - at JsonApiDotNetCore.Formatters.JsonApiReader.ReadAsync(InputFormatterContext context) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs:line 39 -fail: JsonApiDotNetCore.Formatters.JsonApiReader[0] - An error occurred while de-serializing the payload -JsonApiDotNetCore.Internal.JsonApiException: Failed to deserialize request body ---> System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[JsonApiDotNetCore.Models.ResourceIdentifierObject]' to type 'System.Collections.Generic.List`1[System.Collections.Generic.Dictionary`2[System.String,System.Object]]'. - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.SetHasManyRelationship(Object entity, PropertyInfo[] entityProperties, RelationshipAttribute attr, ContextEntity contextEntity, Dictionary`2 relationships) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 216 - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.SetRelationships(Object entity, ContextEntity contextEntity, Dictionary`2 relationships) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 156 - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.DocumentToObject(DocumentData data) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 92 - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.Deserialize(String requestBody) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 33 - --- End of inner exception stack trace --- - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.Deserialize(String requestBody) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 38 - at JsonApiDotNetCore.Formatters.JsonApiReader.ReadAsync(InputFormatterContext context) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs:line 39 -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoCollectionsController.PostAsync (JsonApiDotNetCoreExample) with arguments () - ModelState is Invalid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoCollectionsController.PostAsync (JsonApiDotNetCoreExample) with arguments () - ModelState is Invalid -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoCollectionsController.PostAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.StatusCodeResult. -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 422 -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 422 -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoCollectionsController.PostAsync (JsonApiDotNetCoreExample) in 22.188ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoCollectionsController.PostAsync (JsonApiDotNetCoreExample) in 22.188ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 42.033ms 422 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 42.033ms 422 -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -[xUnit.net 00:00:12.5112580] JsonApiDotNetCore.Internal.JsonApiException : Failed to deserialize request body -[xUnit.net 00:00:12.5115070] ---- System.NullReferenceException : Object reference not set to an instance of an object. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -[xUnit.net 00:00:12.5136610] Stack Trace: -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -[xUnit.net 00:00:12.5158820] /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs(38,0): at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.Deserialize(String requestBody) -[xUnit.net 00:00:12.5161330] /Users/jarednance/dev/json-api-dotnet-core/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs(273,0): at JsonApiDotNetCoreExampleTests.Acceptance.Spec.CreatingDataTests.d__8.MoveNext() -[xUnit.net 00:00:12.5165710] --- End of stack trace from previous location where exception was thrown --- -[xUnit.net 00:00:12.5167200] at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() -[xUnit.net 00:00:12.5168760] at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) -[xUnit.net 00:00:12.5170850] --- End of stack trace from previous location where exception was thrown --- -[xUnit.net 00:00:12.5171820] at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() -[xUnit.net 00:00:12.5172640] at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) -[xUnit.net 00:00:12.5174340] --- End of stack trace from previous location where exception was thrown --- -[xUnit.net 00:00:12.5175190] at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() -[xUnit.net 00:00:12.5175960] at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) -[xUnit.net 00:00:12.5180580] ----- Inner Stack Trace ----- -[xUnit.net 00:00:12.5182020] /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs(32,0): at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.Deserialize(String requestBody) -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 POST http://localhost/api/v1/todo-items application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 POST http://localhost/api/v1/todo-items application/vnd.api+json -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample)' with id '72eb1fba-bd67-40f7-841e-5d45f4d87655' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample) -dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[1] - Selected input formatter 'JsonApiDotNetCore.Formatters.JsonApiInputFormatter' for content type 'application/vnd.api+json'. -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample) with arguments (JsonApiDotNetCoreExample.Models.TodoItem) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample) with arguments (JsonApiDotNetCoreExample.Models.TodoItem) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (11ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (11ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6) - RETURNING "Id", "CreatedDate"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.CreatedResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample) in 78.019ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 102.786ms 201 application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample) in 78.019ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 102.786ms 201 application/vnd.api+json -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 POST http://localhost/api/v1/todo-items application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 POST http://localhost/api/v1/todo-items application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample) with arguments (JsonApiDotNetCoreExample.Models.TodoItem) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample) with arguments (JsonApiDotNetCoreExample.Models.TodoItem) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (11ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("Id", "AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (11ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("Id", "AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "CreatedDate"; -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample) in 72.862ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 94.538ms 201 application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample) in 72.862ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 94.538ms 201 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _1 in DbSet - select [_1]).LastOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _1 in DbSet - select [_1]).LastOrDefault()' -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'LastOrDefault()' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'LastOrDefault()' could not be translated and will be evaluated locally. -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _ToSequence(TodoItem LastOrDefault(IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t", - shaper: UnbufferedEntityShaper))), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 DELETE http://localhost/api/v1/todo-items/10099 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 DELETE http://localhost/api/v1/todo-items/10099 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample)' with id 'fe31e5b4-7432-4ff5-978a-cd27e2b93135' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '3c77838f-36d1-41ad-b93d-e79ea0456a72' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample) with arguments (10099) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample) with arguments (10099) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: UnbufferedEntityShaper), - cancellationToken: queryContext.CancellationToken)), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (16ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (16ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.NotFoundResult. -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 404 -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 404 -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample) in 26.655ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample) in 26.655ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 58.036ms 404 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 58.036ms 404 -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2611?include=owner -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2611?include=owner -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id 'da4f66ae-3f53-4439-80dc-28886ee0c729' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id 'df4d22d4-25aa-4cc4-b517-33a346cabff2' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2611) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2611) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]) - .Include("Owner") - .FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[e].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - join Person e.Owner in DbSet - on Property([e], "OwnerId") equals (Nullable)Property([e.Owner], "Id") into e.Owner_group - from Person e.Owner in - (from Person e.Owner_groupItem in [e.Owner_group] - select [e.Owner_groupItem]).DefaultIfEmpty() - where bool [e].Id.Equals((object)__id_0) - select TodoItem _Include( - queryContext: queryContext, - entity: [e], - included: new object[]{ [e.Owner] }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )).FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task FirstOrDefault( - source: IAsyncEnumerable _Select( - source: IAsyncEnumerable> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>), - selector: (TransparentIdentifier t1) => TodoItem _Include( - queryContext: queryContext, - entity: t1.Outer, - included: new object[]{ t1.Inner }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )), - cancellationToken: Unhandled parameter: queryContext.CancellationToken)), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (9ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (9ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 81.295ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 101.754ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 81.295ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 101.754ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; - DELETE FROM "TodoItems" - WHERE "Id" = @p1; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; - DELETE FROM "TodoItems" - WHERE "Id" = @p6; - DELETE FROM "TodoItems" - WHERE "Id" = @p7; - DELETE FROM "TodoItems" - WHERE "Id" = @p8; - DELETE FROM "TodoItems" - WHERE "Id" = @p9; - DELETE FROM "TodoItems" - WHERE "Id" = @p10; - DELETE FROM "TodoItems" - WHERE "Id" = @p11; - DELETE FROM "TodoItems" - WHERE "Id" = @p12; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; - DELETE FROM "TodoItems" - WHERE "Id" = @p1; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; - DELETE FROM "TodoItems" - WHERE "Id" = @p6; - DELETE FROM "TodoItems" - WHERE "Id" = @p7; - DELETE FROM "TodoItems" - WHERE "Id" = @p8; - DELETE FROM "TodoItems" - WHERE "Id" = @p9; - DELETE FROM "TodoItems" - WHERE "Id" = @p10; - DELETE FROM "TodoItems" - WHERE "Id" = @p11; - DELETE FROM "TodoItems" - WHERE "Id" = @p12; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; - DELETE FROM "TodoItems" - WHERE "Id" = @p1; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; - DELETE FROM "TodoItems" - WHERE "Id" = @p6; - DELETE FROM "TodoItems" - WHERE "Id" = @p7; - DELETE FROM "TodoItems" - WHERE "Id" = @p8; - DELETE FROM "TodoItems" - WHERE "Id" = @p9; - DELETE FROM "TodoItems" - WHERE "Id" = @p10; - DELETE FROM "TodoItems" - WHERE "Id" = @p11; - DELETE FROM "TodoItems" - WHERE "Id" = @p12; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample)' with id '45c59ea2-28ca-4bbb-b184-0cd79d273bed' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 41.443ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 41.443ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 57.056ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 57.056ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2612/owner -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2612/owner -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}/{relationshipName}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) with arguments (2612, owner) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) with arguments (2612, owner) - ModelState is Valid -trce: JsonApiDotNetCore.Services.EntityResourceService[0] - Looking up 'Owner'... -dbug: JsonApiDotNetCore.Data.DefaultEntityRepository[0] - [JADN] GetAndIncludeAsync(2612, Owner) -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in - (from TodoItem _1 in DbSet - select [_1]).Include("Owner") - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[_1].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - join Person e.Owner in DbSet - on Property([e], "OwnerId") equals (Nullable)Property([e.Owner], "Id") into e.Owner_group - from Person e.Owner in - (from Person e.Owner_groupItem in [e.Owner_group] - select [e.Owner_groupItem]).DefaultIfEmpty() - where bool [e].Id.Equals((object)__id_0) - select TodoItem _Include( - queryContext: queryContext, - entity: [e], - included: new object[]{ [e.Owner] }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _Select( - source: IAsyncEnumerable> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>), - selector: (TransparentIdentifier t1) => TodoItem _Include( - queryContext: queryContext, - entity: t1.Outer, - included: new object[]{ t1.Inner }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )), - cancellationToken: Unhandled parameter: queryContext.CancellationToken)), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -fail: JsonApiDotNetCore.Middleware.JsonApiExceptionFilter[0] - An unhandled exception occurred during the request -JsonApiDotNetCore.Internal.JsonApiException: Relationship Owner not found. - at JsonApiDotNetCore.Services.EntityResourceService`2.d__9.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/EntityResourceService.cs:line 102 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Controllers.BaseJsonApiController`2.d__15.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs:line 141 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Controllers.JsonApiController`2.d__6.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 73 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() -fail: JsonApiDotNetCore.Middleware.JsonApiExceptionFilter[0] - An unhandled exception occurred during the request -JsonApiDotNetCore.Internal.JsonApiException: Relationship Owner not found. - at JsonApiDotNetCore.Services.EntityResourceService`2.d__9.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/EntityResourceService.cs:line 102 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Controllers.BaseJsonApiController`2.d__15.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs:line 141 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Controllers.JsonApiController`2.d__6.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 73 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) in 58.256ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) in 58.256ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 78.585ms 404 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 78.585ms 404 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -Failed JsonApiDotNetCoreExampleTests.Acceptance.Spec.CreatingDataTests.Can_Create_And_Set_HasMany_Relationships -Error Message: - JsonApiDotNetCore.Internal.JsonApiException : Failed to deserialize request body ----- System.NullReferenceException : Object reference not set to an instance of an object. -Stack Trace: - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.Deserialize(String requestBody) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 38 - at JsonApiDotNetCoreExampleTests.Acceptance.Spec.CreatingDataTests.d__8.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs:line 273 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) ------ Inner Stack Trace ----- - at JsonApiDotNetCore.Serialization.JsonApiDeSerializer.Deserialize(String requestBody) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs:line 32 -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2613/owner -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2613/owner -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}/{relationshipName}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) with arguments (2613, owner) - ModelState is Valid -trce: JsonApiDotNetCore.Services.EntityResourceService[0] - Looking up 'Owner'... -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) with arguments (2613, owner) - ModelState is Valid -dbug: JsonApiDotNetCore.Data.DefaultEntityRepository[0] - [JADN] GetAndIncludeAsync(2613, Owner) -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in - (from TodoItem _1 in DbSet - select [_1]).Include("Owner") - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[_1].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - join Person e.Owner in DbSet - on Property([e], "OwnerId") equals (Nullable)Property([e.Owner], "Id") into e.Owner_group - from Person e.Owner in - (from Person e.Owner_groupItem in [e.Owner_group] - select [e.Owner_groupItem]).DefaultIfEmpty() - where bool [e].Id.Equals((object)__id_0) - select TodoItem _Include( - queryContext: queryContext, - entity: [e], - included: new object[]{ [e.Owner] }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _Select( - source: IAsyncEnumerable> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>), - selector: (TransparentIdentifier t1) => TodoItem _Include( - queryContext: queryContext, - entity: t1.Outer, - included: new object[]{ t1.Inner }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )), - cancellationToken: Unhandled parameter: queryContext.CancellationToken)), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (11ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (11ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) in 56.604ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipAsync (JsonApiDotNetCoreExample) in 56.604ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 74.256ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 74.256ms 200 application/vnd.api+json -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?unknownKey=value -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?unknownKey=value -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample)' with id '154c85f6-801d-4115-8e42-da8fe75f1bea' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -fail: JsonApiDotNetCore.Middleware.JsonApiExceptionFilter[0] - An unhandled exception occurred during the request -JsonApiDotNetCore.Internal.JsonApiException: [unknownKey, value] is not a valid query. - at JsonApiDotNetCore.Services.QueryParser.Parse(IQueryCollection query) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/QueryParser.cs:line 75 - at JsonApiDotNetCore.Services.JsonApiContext.ApplyContext[T](Object controller) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/JsonApiContext.cs:line 70 - at JsonApiDotNetCore.Controllers.BaseJsonApiController`2..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs:line 56 - at JsonApiDotNetCore.Controllers.JsonApiController`2..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 40 - at JsonApiDotNetCore.Controllers.JsonApiController`1..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 17 - at JsonApiDotNetCoreExample.Controllers.TodoItemsController..ctor(IJsonApiContext jsonApiContext, IResourceService`1 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs:line 14 - at lambda_method(Closure , IServiceProvider , Object[] ) - at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(ControllerContext controllerContext) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() -fail: JsonApiDotNetCore.Middleware.JsonApiExceptionFilter[0] - An unhandled exception occurred during the request -JsonApiDotNetCore.Internal.JsonApiException: [unknownKey, value] is not a valid query. - at JsonApiDotNetCore.Services.QueryParser.Parse(IQueryCollection query) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/QueryParser.cs:line 75 - at JsonApiDotNetCore.Services.JsonApiContext.ApplyContext[T](Object controller) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/JsonApiContext.cs:line 70 - at JsonApiDotNetCore.Controllers.BaseJsonApiController`2..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs:line 56 - at JsonApiDotNetCore.Controllers.JsonApiController`2..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 40 - at JsonApiDotNetCore.Controllers.JsonApiController`1..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 17 - at JsonApiDotNetCoreExample.Controllers.TodoItemsController..ctor(IJsonApiContext jsonApiContext, IResourceService`1 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs:line 14 - at lambda_method(Closure , IServiceProvider , Object[] ) - at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(ControllerContext controllerContext) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 4.858ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 4.858ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 24.057ms 400 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 24.057ms 400 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2614?fields[todo-items]=description,created-date -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2614?fields[todo-items]=description,created-date -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id 'ce1f18f1-2c9d-469e-8706-cdda1d627b0c' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '736779bb-b124-40ac-a9e5-58f8ea4bdcc3' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2614) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2614) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem model in DbSet - where bool new TodoItem{ - Id = [model].Id, - Description = [model].Description, - CreatedDate = [model].CreatedDate - } - .Id.Equals((object)__id_0) - select new TodoItem{ - Id = [model].Id, - Description = [model].Description, - CreatedDate = [model].CreatedDate - } - ).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem model in DbSet - where bool new TodoItem{ - Id = [model].Id, - Description = [model].Description, - CreatedDate = [model].CreatedDate - } - .Id.Equals((object)__id_0) - select new TodoItem{ - Id = [model].Id, - Description = [model].Description, - CreatedDate = [model].CreatedDate - } - ).SingleOrDefault()' -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'where new TodoItem() {Id = [model].Id, Description = [model].Description, CreatedDate = [model].CreatedDate}.Id.Equals(Convert(__id_0, Object))' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'where new TodoItem() {Id = [model].Id, Description = [model].Description, CreatedDate = [model].CreatedDate}.Id.Equals(Convert(__id_0, Object))' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'SingleOrDefault()' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'SingleOrDefault()' could not be translated and will be evaluated locally. -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _Select( - source: IAsyncEnumerable _Where( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "model"."Id", "model"."Description", "model"."CreatedDate" - FROM "TodoItems" AS "model", - shaper: ValueBufferShaper), - predicate: (ValueBuffer model) => bool new TodoItem{ - Id = int TryReadValue(model, 0, TodoItem.Id), - Description = string TryReadValue(model, 1, TodoItem.Description), - CreatedDate = DateTime TryReadValue(model, 2, TodoItem.CreatedDate) - } - .Id.Equals((object)int GetParameterValue( - queryContext: queryContext, - parameterName: "__id_0"))), - selector: (ValueBuffer model) => new TodoItem{ - Id = int TryReadValue(model, 0, TodoItem.Id), - Description = string TryReadValue(model, 1, TodoItem.Description), - CreatedDate = DateTime TryReadValue(model, 2, TodoItem.CreatedDate) - } - ), - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "model"."Id", "model"."Description", "model"."CreatedDate" - FROM "TodoItems" AS "model" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "model"."Id", "model"."Description", "model"."CreatedDate" - FROM "TodoItems" AS "model" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "model"."Id", "model"."Description", "model"."CreatedDate" - FROM "TodoItems" AS "model" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 105.374ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 105.374ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 125.325ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 125.325ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - 'from TodoItem t in DbSet - where [t].Id == 2615 - select new TodoItem{ - Id = [t].Id, - Description = [t].Description, - CreatedDate = [t].CreatedDate, - AchievedDate = [t].AchievedDate - } - ' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - 'from TodoItem t in DbSet - where [t].Id == 2615 - select new TodoItem{ - Id = [t].Id, - Description = [t].Description, - CreatedDate = [t].CreatedDate, - AchievedDate = [t].AchievedDate - } - ' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."Description", "t"."CreatedDate", "t"."AchievedDate" - FROM "TodoItems" AS "t" - WHERE "t"."Id" = 2615, - shaper: TypedProjectionShaper), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem t in DbSet - where [t].Id == __todoItem_Id_0 - select new TodoItem{ - Id = [t].Id, - Description = [t].Description, - CreatedDate = [t].CreatedDate, - AchievedDate = [t].AchievedDate - } - ).First()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem t in DbSet - where [t].Id == __todoItem_Id_0 - select new TodoItem{ - Id = [t].Id, - Description = [t].Description, - CreatedDate = [t].CreatedDate, - AchievedDate = [t].AchievedDate - } - ).First()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task First( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."Description", "t"."CreatedDate", "t"."AchievedDate" - FROM "TodoItems" AS "t" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 1, - shaper: TypedProjectionShaper), - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__todoItem_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."Description", "t"."CreatedDate", "t"."AchievedDate" - FROM "TodoItems" AS "t" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__todoItem_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."Description", "t"."CreatedDate", "t"."AchievedDate" - FROM "TodoItems" AS "t" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__todoItem_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."Description", "t"."CreatedDate", "t"."AchievedDate" - FROM "TodoItems" AS "t" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 1 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 PATCH http://localhost/api/v1/todo-items/2616 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 PATCH http://localhost/api/v1/todo-items/2616 application/vnd.api+json -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample)' with id '99416189-fd56-43fd-accb-b675b7d46314' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '7d80e15e-9750-4047-b86c-88bfc61fd12f' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) -dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[1] - Selected input formatter 'JsonApiDotNetCore.Formatters.JsonApiInputFormatter' for content type 'application/vnd.api+json'. -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) with arguments (2616, JsonApiDotNetCoreExample.Models.TodoItem) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) with arguments (2616, JsonApiDotNetCoreExample.Models.TodoItem) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: UnbufferedEntityShaper), - cancellationToken: queryContext.CancellationToken)), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (11ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (11ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p1='?', @p0='?'], CommandType='Text', CommandTimeout='30'] - UPDATE "TodoItems" SET "OwnerId" = @p0 - WHERE "Id" = @p1; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p1='?', @p0='?'], CommandType='Text', CommandTimeout='30'] - UPDATE "TodoItems" SET "OwnerId" = @p0 - WHERE "Id" = @p1; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p1='?', @p0='?'], CommandType='Text', CommandTimeout='30'] - UPDATE "TodoItems" SET "OwnerId" = @p0 - WHERE "Id" = @p1; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) in 95.019ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) in 95.019ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 116.16ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 116.16ms 200 application/vnd.api+json -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem t in - (from TodoItem _1 in DbSet - select [_1]) - .AsNoTracking() - .Include("Owner") - where [t].Id == __todoItem_Id_0 - select [t]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[_1].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem t in DbSet - join Person t.Owner in DbSet - on Property([t], "OwnerId") equals (Nullable)Property([t.Owner], "Id") into t.Owner_group - from Person t.Owner in - (from Person t.Owner_groupItem in [t.Owner_group] - select [t.Owner_groupItem]).DefaultIfEmpty() - where [t].Id == __todoItem_Id_0 - select TodoItem _Include( - queryContext: queryContext, - entity: [t], - included: new object[]{ [t.Owner] }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - return !(bool ReferenceEquals(included[0], null)) ? - { - entity.Owner = (Person)included[0] - return bool ClrICollectionAccessor, TodoItem>.Add( - instance: included[0], - value: entity) - } - : default(bool) - } - )).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _ToSequence(TodoItem SingleOrDefault(IEnumerable _Select( - source: IEnumerable> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 2, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>), - selector: (TransparentIdentifier t1) => TodoItem _Include( - queryContext: queryContext, - entity: t1.Outer, - included: new object[]{ t1.Inner }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - return !(bool ReferenceEquals(included[0], null)) ? - { - entity.Owner = (Person)included[0] - return bool ClrICollectionAccessor, TodoItem>.Add( - instance: included[0], - value: entity) - } - : default(bool) - } - )))), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__todoItem_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__todoItem_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__todoItem_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 PATCH http://localhost/api/v1/todo-items/2716 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 PATCH http://localhost/api/v1/todo-items/2716 application/vnd.api+json -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample)' with id '2a9a328a-ccd2-441f-86b9-778e783257c7' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id 'f400e30f-a558-41ca-9432-b889d803f5d2' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) -dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[1] - Selected input formatter 'JsonApiDotNetCore.Formatters.JsonApiInputFormatter' for content type 'application/vnd.api+json'. -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) with arguments (2716, JsonApiDotNetCoreExample.Models.TodoItem) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) with arguments (2716, JsonApiDotNetCoreExample.Models.TodoItem) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: UnbufferedEntityShaper), - cancellationToken: queryContext.CancellationToken)), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (9ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (9ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.NotFoundResult. -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 404 -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 404 -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) in 27.817ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample) in 27.817ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 47.257ms 404 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 47.257ms 404 -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 PATCH http://localhost/api/v1/todo-items/2617/relationships/owner application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 PATCH http://localhost/api/v1/todo-items/2617/relationships/owner application/vnd.api+json -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}/relationships/{relationshipName}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetRelationshipsAsync (JsonApiDotNetCoreExample)' with id '3fde0ec7-9039-455f-910c-e568544f788a' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) -dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[1] - Selected input formatter 'JsonApiDotNetCore.Formatters.JsonApiInputFormatter' for content type 'application/vnd.api+json'. -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) with arguments (2617, owner, System.Collections.Generic.List`1[JsonApiDotNetCore.Models.DocumentData]) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) with arguments (2617, owner, System.Collections.Generic.List`1[JsonApiDotNetCore.Models.DocumentData]) - ModelState is Valid -dbug: JsonApiDotNetCore.Data.DefaultEntityRepository[0] - [JADN] GetAndIncludeAsync(2617, Owner) -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in - (from TodoItem _1 in DbSet - select [_1]).Include("Owner") - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[_1].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - join Person e.Owner in DbSet - on Property([e], "OwnerId") equals (Nullable)Property([e.Owner], "Id") into e.Owner_group - from Person e.Owner in - (from Person e.Owner_groupItem in [e.Owner_group] - select [e.Owner_groupItem]).DefaultIfEmpty() - where bool [e].Id.Equals((object)__id_0) - select TodoItem _Include( - queryContext: queryContext, - entity: [e], - included: new object[]{ [e.Owner] }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _Select( - source: IAsyncEnumerable> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>), - selector: (TransparentIdentifier t1) => TodoItem _Include( - queryContext: queryContext, - entity: t1.Outer, - included: new object[]{ t1.Inner }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )), - cancellationToken: Unhandled parameter: queryContext.CancellationToken)), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (12ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (12ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person x in DbSet - where __First_0 == [x].StringId - select [x]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person x in DbSet - where __First_0 == [x].StringId - select [x]).SingleOrDefault()' -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'where (__First_0 == [x].StringId)' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'where (__First_0 == [x].StringId)' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'SingleOrDefault()' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'SingleOrDefault()' could not be translated and will be evaluated locally. -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _ToSequence(Person SingleOrDefault(IEnumerable _Where( - source: IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "x"."Id", "x"."FirstName", "x"."LastName" - FROM "People" AS "x", - shaper: UnbufferedEntityShaper), - predicate: (Person x) => string GetParameterValue( - queryContext: queryContext, - parameterName: "__First_0") == x.StringId))), - queryContext: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "x"."Id", "x"."FirstName", "x"."LastName" - FROM "People" AS "x" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "x"."Id", "x"."FirstName", "x"."LastName" - FROM "People" AS "x" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "x"."Id", "x"."FirstName", "x"."LastName" - FROM "People" AS "x" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p1='?', @p0='?'], CommandType='Text', CommandTimeout='30'] - UPDATE "TodoItems" SET "OwnerId" = @p0 - WHERE "Id" = @p1; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p1='?', @p0='?'], CommandType='Text', CommandTimeout='30'] - UPDATE "TodoItems" SET "OwnerId" = @p0 - WHERE "Id" = @p1; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p1='?', @p0='?'], CommandType='Text', CommandTimeout='30'] - UPDATE "TodoItems" SET "OwnerId" = @p0 - WHERE "Id" = @p1; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchRelationshipsAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkResult. -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 200 -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 200 -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) in 128.36ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) in 128.36ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 147.825ms 200 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 147.825ms 200 -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem t in - (from TodoItem t in DbSet - select [t]).Include("Owner") - where [t].Id == __todoItem_Id_0 - select [t]).Single()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[t].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem t in DbSet - join Person t.Owner in DbSet - on Property([t], "OwnerId") equals (Nullable)Property([t.Owner], "Id") into t.Owner_group - from Person t.Owner in - (from Person t.Owner_groupItem in [t.Owner_group] - select [t.Owner_groupItem]).DefaultIfEmpty() - where [t].Id == __todoItem_Id_0 - select TodoItem _Include( - queryContext: queryContext, - entity: [t], - included: new object[]{ [t.Owner] }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )).Single()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _ToSequence(TodoItem Single(IEnumerable _Select( - source: IEnumerable> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 2, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>), - selector: (TransparentIdentifier t1) => TodoItem _Include( - queryContext: queryContext, - entity: t1.Outer, - included: new object[]{ t1.Inner }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )))), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__todoItem_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__todoItem_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__todoItem_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - WHERE "t"."Id" = @__todoItem_Id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 PATCH http://localhost/api/v1/people/887/relationships/todo-items application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 PATCH http://localhost/api/v1/people/887/relationships/todo-items application/vnd.api+json -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people/{id}/relationships/{relationshipName}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.GetRelationshipsAsync (JsonApiDotNetCoreExample)' with id '55bc06aa-666a-4e45-9dba-c3c04b1e2592' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) -dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[1] - Selected input formatter 'JsonApiDotNetCore.Formatters.JsonApiInputFormatter' for content type 'application/vnd.api+json'. -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) with arguments (887, todo-items, System.Collections.Generic.List`1[JsonApiDotNetCore.Models.DocumentData]) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) with arguments (887, todo-items, System.Collections.Generic.List`1[JsonApiDotNetCore.Models.DocumentData]) - ModelState is Valid -dbug: JsonApiDotNetCore.Data.DefaultEntityRepository[0] - [JADN] GetAndIncludeAsync(887, TodoItems) -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person e in - (from Person _1 in DbSet - select [_1]).Include("TodoItems") - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[_1].TodoItems' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - order by (Nullable)EF.Property(?[e]?, "Id") asc - select Task _IncludeAsync( - queryContext: queryContext, - entity: [e], - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included | CancellationToken ct) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => - from TodoItem e.TodoItems in DbSet - join AnonymousObject _e in - (from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - order by (Nullable)EF.Property(?[e]?, "Id") asc - select new AnonymousObject(new object[]{ (object)EF.Property(?[e]?, "Id") })).Take(1) - on Property([e.TodoItems], "OwnerId") equals (Nullable)object [_e].GetValue(0) - order by object [_e].GetValue(0) asc - select [e.TodoItems], - cancellationToken: ct) - } - , - cancellationToken: ct).Result).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _SelectAsync( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 2, - shaper: BufferedEntityShaper), - selector: (Person e | CancellationToken ct) => Task _ExecuteAsync( - taskFactories: new Func>[]{ () => Task _ToObjectTask(Task _IncludeAsync( - queryContext: queryContext, - entity: e, - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included | CancellationToken ct) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id", - shaper: BufferedEntityShaper), - cancellationToken: ct) - } - , - cancellationToken: Unhandled parameter: ct)) }, - selector: (Object[] results) => (Person)results[0])), - cancellationToken: Unhandled parameter: queryContext.CancellationToken)), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (5ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (5ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - 'from TodoItem x in DbSet - where - (from string _1 in __relationshipIds_0 - select [_1]).Contains([x].StringId) - select [x]' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - 'from TodoItem x in DbSet - where - (from string _1 in __relationshipIds_0 - select [_1]).Contains([x].StringId) - select [x]' -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'where {__relationshipIds_0 => Contains([x].StringId)}' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'where {__relationshipIds_0 => Contains([x].StringId)}' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'Contains([x].StringId)' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'Contains([x].StringId)' could not be translated and will be evaluated locally. -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _Where( - source: IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "x"."Id", "x"."AchievedDate", "x"."AssigneeId", "x"."CollectionId", "x"."CreatedDate", "x"."Description", "x"."GuidProperty", "x"."Ordinal", "x"."OwnerId" - FROM "TodoItems" AS "x", - shaper: UnbufferedEntityShaper), - predicate: (TodoItem x) => bool Contains( - source: IEnumerable GetParameterValue( - queryContext: queryContext, - parameterName: "__relationshipIds_0"), - value: x.StringId)), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "x"."Id", "x"."AchievedDate", "x"."AssigneeId", "x"."CollectionId", "x"."CreatedDate", "x"."Description", "x"."GuidProperty", "x"."Ordinal", "x"."OwnerId" - FROM "TodoItems" AS "x" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "x"."Id", "x"."AchievedDate", "x"."AssigneeId", "x"."CollectionId", "x"."CreatedDate", "x"."Description", "x"."GuidProperty", "x"."Ordinal", "x"."OwnerId" - FROM "TodoItems" AS "x" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "x"."Id", "x"."AchievedDate", "x"."AssigneeId", "x"."CollectionId", "x"."CreatedDate", "x"."Description", "x"."GuidProperty", "x"."Ordinal", "x"."OwnerId" - FROM "TodoItems" AS "x" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p1='?', @p0='?'], CommandType='Text', CommandTimeout='30'] - UPDATE "TodoItems" SET "OwnerId" = @p0 - WHERE "Id" = @p1; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p1='?', @p0='?'], CommandType='Text', CommandTimeout='30'] - UPDATE "TodoItems" SET "OwnerId" = @p0 - WHERE "Id" = @p1; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p1='?', @p0='?'], CommandType='Text', CommandTimeout='30'] - UPDATE "TodoItems" SET "OwnerId" = @p0 - WHERE "Id" = @p1; -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.PeopleController.PatchRelationshipsAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkResult. -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 200 -info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1] - Executing HttpStatusCodeResult, setting HTTP status code 200 -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) in 222.821ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.PatchRelationshipsAsync (JsonApiDotNetCoreExample) in 222.821ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 254.919ms 200 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 254.919ms 200 -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person p in - (from Person p in DbSet - select [p]).Include("TodoItems") - where [p].Id == __person_Id_0 - select [p]).Single()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[p].TodoItems' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person p in DbSet - where [p].Id == __person_Id_0 - order by (Nullable)EF.Property(?[p]?, "Id") asc - select Person _Include( - queryContext: queryContext, - entity: [p], - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Void queryContext.QueryBuffer.IncludeCollection( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: () => - from TodoItem p.TodoItems in DbSet - join AnonymousObject _p in - (from Person p in DbSet - where [p].Id == __person_Id_0 - order by (Nullable)EF.Property(?[p]?, "Id") asc - select new AnonymousObject(new object[]{ (object)EF.Property(?[p]?, "Id") })).Take(1) - on Property([p.TodoItems], "OwnerId") equals (Nullable)object [_p].GetValue(0) - order by object [_p].GetValue(0) asc - select [p.TodoItems]) - } - )).Single()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _ToSequence(Person Single(IEnumerable _Select( - source: IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - WHERE "p"."Id" = @__person_Id_0 - ORDER BY "p"."Id" - LIMIT 2, - shaper: BufferedEntityShaper), - selector: (Person p) => Person _Include( - queryContext: queryContext, - entity: p, - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Void queryContext.QueryBuffer.IncludeCollection( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: () => IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p.TodoItems"."Id", "p.TodoItems"."AchievedDate", "p.TodoItems"."AssigneeId", "p.TodoItems"."CollectionId", "p.TodoItems"."CreatedDate", "p.TodoItems"."Description", "p.TodoItems"."GuidProperty", "p.TodoItems"."Ordinal", "p.TodoItems"."OwnerId" - FROM "TodoItems" AS "p.TodoItems" - INNER JOIN ( - SELECT "p0"."Id" - FROM "People" AS "p0" - WHERE "p0"."Id" = @__person_Id_0 - ORDER BY "p0"."Id" - LIMIT 1 - ) AS "t" ON "p.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id", - shaper: BufferedEntityShaper)) - } - )))), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__person_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - WHERE "p"."Id" = @__person_Id_0 - ORDER BY "p"."Id" - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__person_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - WHERE "p"."Id" = @__person_Id_0 - ORDER BY "p"."Id" - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__person_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - WHERE "p"."Id" = @__person_Id_0 - ORDER BY "p"."Id" - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__person_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p.TodoItems"."Id", "p.TodoItems"."AchievedDate", "p.TodoItems"."AssigneeId", "p.TodoItems"."CollectionId", "p.TodoItems"."CreatedDate", "p.TodoItems"."Description", "p.TodoItems"."GuidProperty", "p.TodoItems"."Ordinal", "p.TodoItems"."OwnerId" - FROM "TodoItems" AS "p.TodoItems" - INNER JOIN ( - SELECT "p0"."Id" - FROM "People" AS "p0" - WHERE "p0"."Id" = @__person_Id_0 - ORDER BY "p0"."Id" - LIMIT 1 - ) AS "t" ON "p.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__person_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p.TodoItems"."Id", "p.TodoItems"."AchievedDate", "p.TodoItems"."AssigneeId", "p.TodoItems"."CollectionId", "p.TodoItems"."CreatedDate", "p.TodoItems"."Description", "p.TodoItems"."GuidProperty", "p.TodoItems"."Ordinal", "p.TodoItems"."OwnerId" - FROM "TodoItems" AS "p.TodoItems" - INNER JOIN ( - SELECT "p0"."Id" - FROM "People" AS "p0" - WHERE "p0"."Id" = @__person_Id_0 - ORDER BY "p0"."Id" - LIMIT 1 - ) AS "t" ON "p.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__person_Id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p.TodoItems"."Id", "p.TodoItems"."AchievedDate", "p.TodoItems"."AssigneeId", "p.TodoItems"."CollectionId", "p.TodoItems"."CreatedDate", "p.TodoItems"."Description", "p.TodoItems"."GuidProperty", "p.TodoItems"."Ordinal", "p.TodoItems"."OwnerId" - FROM "TodoItems" AS "p.TodoItems" - INNER JOIN ( - SELECT "p0"."Id" - FROM "People" AS "p0" - WHERE "p0"."Id" = @__person_Id_0 - ORDER BY "p0"."Id" - LIMIT 1 - ) AS "t" ON "p.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - 'from Person _0 in DbSet - select [_0]' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - 'from Person _0 in DbSet - select [_0]' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p", - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p0; - DELETE FROM "People" - WHERE "Id" = @p1; - DELETE FROM "People" - WHERE "Id" = @p2; - DELETE FROM "People" - WHERE "Id" = @p3; - DELETE FROM "People" - WHERE "Id" = @p4; - DELETE FROM "People" - WHERE "Id" = @p5; - DELETE FROM "People" - WHERE "Id" = @p6; - DELETE FROM "People" - WHERE "Id" = @p7; - DELETE FROM "People" - WHERE "Id" = @p8; - DELETE FROM "People" - WHERE "Id" = @p9; - DELETE FROM "People" - WHERE "Id" = @p10; - DELETE FROM "People" - WHERE "Id" = @p11; - DELETE FROM "People" - WHERE "Id" = @p12; - DELETE FROM "People" - WHERE "Id" = @p13; - DELETE FROM "People" - WHERE "Id" = @p14; - DELETE FROM "People" - WHERE "Id" = @p15; - DELETE FROM "People" - WHERE "Id" = @p16; - DELETE FROM "People" - WHERE "Id" = @p17; - DELETE FROM "People" - WHERE "Id" = @p18; - DELETE FROM "People" - WHERE "Id" = @p19; - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p20, @p21) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p22; - DELETE FROM "TodoItems" - WHERE "Id" = @p23; - DELETE FROM "TodoItems" - WHERE "Id" = @p24; - DELETE FROM "TodoItems" - WHERE "Id" = @p25; - DELETE FROM "TodoItems" - WHERE "Id" = @p26; - DELETE FROM "TodoItems" - WHERE "Id" = @p27; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p0; - DELETE FROM "People" - WHERE "Id" = @p1; - DELETE FROM "People" - WHERE "Id" = @p2; - DELETE FROM "People" - WHERE "Id" = @p3; - DELETE FROM "People" - WHERE "Id" = @p4; - DELETE FROM "People" - WHERE "Id" = @p5; - DELETE FROM "People" - WHERE "Id" = @p6; - DELETE FROM "People" - WHERE "Id" = @p7; - DELETE FROM "People" - WHERE "Id" = @p8; - DELETE FROM "People" - WHERE "Id" = @p9; - DELETE FROM "People" - WHERE "Id" = @p10; - DELETE FROM "People" - WHERE "Id" = @p11; - DELETE FROM "People" - WHERE "Id" = @p12; - DELETE FROM "People" - WHERE "Id" = @p13; - DELETE FROM "People" - WHERE "Id" = @p14; - DELETE FROM "People" - WHERE "Id" = @p15; - DELETE FROM "People" - WHERE "Id" = @p16; - DELETE FROM "People" - WHERE "Id" = @p17; - DELETE FROM "People" - WHERE "Id" = @p18; - DELETE FROM "People" - WHERE "Id" = @p19; - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p20, @p21) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p22; - DELETE FROM "TodoItems" - WHERE "Id" = @p23; - DELETE FROM "TodoItems" - WHERE "Id" = @p24; - DELETE FROM "TodoItems" - WHERE "Id" = @p25; - DELETE FROM "TodoItems" - WHERE "Id" = @p26; - DELETE FROM "TodoItems" - WHERE "Id" = @p27; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p0; - DELETE FROM "People" - WHERE "Id" = @p1; - DELETE FROM "People" - WHERE "Id" = @p2; - DELETE FROM "People" - WHERE "Id" = @p3; - DELETE FROM "People" - WHERE "Id" = @p4; - DELETE FROM "People" - WHERE "Id" = @p5; - DELETE FROM "People" - WHERE "Id" = @p6; - DELETE FROM "People" - WHERE "Id" = @p7; - DELETE FROM "People" - WHERE "Id" = @p8; - DELETE FROM "People" - WHERE "Id" = @p9; - DELETE FROM "People" - WHERE "Id" = @p10; - DELETE FROM "People" - WHERE "Id" = @p11; - DELETE FROM "People" - WHERE "Id" = @p12; - DELETE FROM "People" - WHERE "Id" = @p13; - DELETE FROM "People" - WHERE "Id" = @p14; - DELETE FROM "People" - WHERE "Id" = @p15; - DELETE FROM "People" - WHERE "Id" = @p16; - DELETE FROM "People" - WHERE "Id" = @p17; - DELETE FROM "People" - WHERE "Id" = @p18; - DELETE FROM "People" - WHERE "Id" = @p19; - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p20, @p21) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p22; - DELETE FROM "TodoItems" - WHERE "Id" = @p23; - DELETE FROM "TodoItems" - WHERE "Id" = @p24; - DELETE FROM "TodoItems" - WHERE "Id" = @p25; - DELETE FROM "TodoItems" - WHERE "Id" = @p26; - DELETE FROM "TodoItems" - WHERE "Id" = @p27; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?', @p38='?', @p39='?', @p40='?', @p41='?', @p42='?', @p43='?', @p44='?', @p45='?', @p46='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p28; - DELETE FROM "People" - WHERE "Id" = @p29; - DELETE FROM "People" - WHERE "Id" = @p30; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p31, @p32, @p33, @p34, @p35, @p36, @p37, @p38) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p39, @p40, @p41, @p42, @p43, @p44, @p45, @p46) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?', @p38='?', @p39='?', @p40='?', @p41='?', @p42='?', @p43='?', @p44='?', @p45='?', @p46='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p28; - DELETE FROM "People" - WHERE "Id" = @p29; - DELETE FROM "People" - WHERE "Id" = @p30; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p31, @p32, @p33, @p34, @p35, @p36, @p37, @p38) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p39, @p40, @p41, @p42, @p43, @p44, @p45, @p46) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?', @p38='?', @p39='?', @p40='?', @p41='?', @p42='?', @p43='?', @p44='?', @p45='?', @p46='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p28; - DELETE FROM "People" - WHERE "Id" = @p29; - DELETE FROM "People" - WHERE "Id" = @p30; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p31, @p32, @p33, @p34, @p35, @p36, @p37, @p38) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p39, @p40, @p41, @p42, @p43, @p44, @p45, @p46) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?include=owner -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?include=owner -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample)' with id '5d8ec96c-5e08-48d8-8af5-20d91d1acfd0' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -warn: Microsoft.EntityFrameworkCore.Query[10106] - The Include operation for navigation '[_2].Owner' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information. -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Include("Owner") - .Count()' -warn: Microsoft.EntityFrameworkCore.Query[10106] - The Include operation for navigation '[_2].Owner' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _2 in DbSet - select [_2]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _3 in DbSet - select [_3]) - .Include("Owner") - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _3 in DbSet select [_3]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _3 in DbSet select [_3]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[_3].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _3 in DbSet - join Person t.Owner in DbSet - on Property([_3], "OwnerId") equals (Nullable)Property([t.Owner], "Id") into t.Owner_group - from Person t.Owner in - (from Person t.Owner_groupItem in [t.Owner_group] - select [t.Owner_groupItem]).DefaultIfEmpty() - select TodoItem _Include( - queryContext: queryContext, - entity: [_3], - included: new object[]{ [t.Owner] }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _Select( - source: IAsyncEnumerable> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - LIMIT @__p_1 OFFSET @__p_0, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>), - selector: (TransparentIdentifier t1) => TodoItem _Include( - queryContext: queryContext, - entity: t1.Outer, - included: new object[]{ t1.Inner }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 76.304ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 76.304ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 96.232ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 96.232ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (0ms) [Parameters=[@p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (0ms) [Parameters=[@p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?include=owner -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?include=owner -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample)' with id '5b834c60-041f-4099-83c5-05901ad2c3a8' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Include("Owner") - .Count()' -warn: Microsoft.EntityFrameworkCore.Query[10106] - The Include operation for navigation '[_2].Owner' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information. -warn: Microsoft.EntityFrameworkCore.Query[10106] - The Include operation for navigation '[_2].Owner' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _2 in DbSet - select [_2]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _3 in DbSet - select [_3]) - .Include("Owner") - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _3 in DbSet select [_3]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _3 in DbSet select [_3]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[_3].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _3 in DbSet - join Person t.Owner in DbSet - on Property([_3], "OwnerId") equals (Nullable)Property([t.Owner], "Id") into t.Owner_group - from Person t.Owner in - (from Person t.Owner_groupItem in [t.Owner_group] - select [t.Owner_groupItem]).DefaultIfEmpty() - select TodoItem _Include( - queryContext: queryContext, - entity: [_3], - included: new object[]{ [t.Owner] }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _Select( - source: IAsyncEnumerable> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - LIMIT @__p_1 OFFSET @__p_0, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>), - selector: (TransparentIdentifier t1) => TodoItem _Include( - queryContext: queryContext, - entity: t1.Outer, - included: new object[]{ t1.Inner }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId", "t.Owner"."Id", "t.Owner"."FirstName", "t.Owner"."LastName" - FROM "TodoItems" AS "t" - LEFT JOIN "People" AS "t.Owner" ON "t"."OwnerId" = "t.Owner"."Id" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 80.648ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 80.648ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 100.942ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 100.942ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _1 in DbSet - select [_1]).First()' -warn: Microsoft.EntityFrameworkCore.Query[10103] - Query: '(from Person _1 in DbSet select [_1]).First()' uses First/FirstOrDefault operation without OrderBy and filter which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10103] - Query: '(from Person _1 in DbSet select [_1]).First()' uses First/FirstOrDefault operation without OrderBy and filter which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _1 in DbSet - select [_1]).First()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _ToSequence(Person First(IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT 1, - shaper: UnbufferedEntityShaper))), - queryContext: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT 1 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/888?include=non-existent-relationship -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/888?include=non-existent-relationship -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.PatchAsync (JsonApiDotNetCoreExample)' with id 'e3d89928-9c88-496c-b73a-f712ea6b3a37' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.DeleteAsync (JsonApiDotNetCoreExample)' with id '0564ca1d-138d-4994-89ff-4c633b77efa6' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments (888) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments (888) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -fail: JsonApiDotNetCore.Middleware.JsonApiExceptionFilter[0] - An unhandled exception occurred during the request -JsonApiDotNetCore.Internal.JsonApiException: Invalid relationship non-existent-relationship on people - at JsonApiDotNetCore.Data.DefaultEntityRepository`2.Include(IQueryable`1 entities, String relationshipName) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs:line 139 - at JsonApiDotNetCore.Services.EntityResourceService`2.<>c__DisplayClass7_0.b__0(String r) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/EntityResourceService.cs:line 77 - at System.Collections.Generic.List`1.ForEach(Action`1 action) - at JsonApiDotNetCore.Services.EntityResourceService`2.d__7.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/EntityResourceService.cs:line 75 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Services.EntityResourceService`2.d__5.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/EntityResourceService.cs:line 63 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Controllers.BaseJsonApiController`2.d__13.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs:line 118 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Controllers.JsonApiController`2.d__4.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 65 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() -fail: JsonApiDotNetCore.Middleware.JsonApiExceptionFilter[0] - An unhandled exception occurred during the request -JsonApiDotNetCore.Internal.JsonApiException: Invalid relationship non-existent-relationship on people - at JsonApiDotNetCore.Data.DefaultEntityRepository`2.Include(IQueryable`1 entities, String relationshipName) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs:line 139 - at JsonApiDotNetCore.Services.EntityResourceService`2.<>c__DisplayClass7_0.b__0(String r) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/EntityResourceService.cs:line 77 - at System.Collections.Generic.List`1.ForEach(Action`1 action) - at JsonApiDotNetCore.Services.EntityResourceService`2.d__7.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/EntityResourceService.cs:line 75 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Services.EntityResourceService`2.d__5.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/EntityResourceService.cs:line 63 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Controllers.BaseJsonApiController`2.d__13.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs:line 118 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at JsonApiDotNetCore.Controllers.JsonApiController`2.d__4.MoveNext() in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 65 ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 14.734ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 14.734ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 32.884ms 400 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 32.884ms 400 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2622?include=owner -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2622?include=owner -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '2e60476c-d745-4d1d-9734-3048cf0e06f7' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '892e3c0c-6a62-4b84-bc3b-630712d4abda' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2622) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2622) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]) - .Include("Owner") - .FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[e].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - join Person e.Owner in DbSet - on Property([e], "OwnerId") equals (Nullable)Property([e.Owner], "Id") into e.Owner_group - from Person e.Owner in - (from Person e.Owner_groupItem in [e.Owner_group] - select [e.Owner_groupItem]).DefaultIfEmpty() - where bool [e].Id.Equals((object)__id_0) - select TodoItem _Include( - queryContext: queryContext, - entity: [e], - included: new object[]{ [e.Owner] }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )).FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task FirstOrDefault( - source: IAsyncEnumerable _Select( - source: IAsyncEnumerable> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>), - selector: (TransparentIdentifier t1) => TodoItem _Include( - queryContext: queryContext, - entity: t1.Outer, - included: new object[]{ t1.Inner }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - return !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - } - )), - cancellationToken: Unhandled parameter: queryContext.CancellationToken)), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 68.602ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 68.602ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 92.782ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 92.782ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/891?include=todo-items -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/891?include=todo-items -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.PatchAsync (JsonApiDotNetCoreExample)' with id '422cb7bd-76e2-4921-a970-fd3b6e53cbc5' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.DeleteAsync (JsonApiDotNetCoreExample)' with id '704b8366-e2fb-4047-91ac-ef75d37aadf9' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments (891) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments (891) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]) - .Include("TodoItems") - .FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[e].TodoItems' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - order by (Nullable)EF.Property(?[e]?, "Id") asc - select Task _IncludeAsync( - queryContext: queryContext, - entity: [e], - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included | CancellationToken ct) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => - from TodoItem e.TodoItems in DbSet - join AnonymousObject _e in - (from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - order by (Nullable)EF.Property(?[e]?, "Id") asc - select new AnonymousObject(new object[]{ (object)EF.Property(?[e]?, "Id") })).Take(1) - on Property([e.TodoItems], "OwnerId") equals (Nullable)object [_e].GetValue(0) - order by object [_e].GetValue(0) asc - select [e.TodoItems], - cancellationToken: ct) - } - , - cancellationToken: ct).Result).FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task FirstOrDefault( - source: IAsyncEnumerable _SelectAsync( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 1, - shaper: BufferedEntityShaper), - selector: (Person e | CancellationToken ct) => Task _ExecuteAsync( - taskFactories: new Func>[]{ () => Task _ToObjectTask(Task _IncludeAsync( - queryContext: queryContext, - entity: e, - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included | CancellationToken ct) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id", - shaper: BufferedEntityShaper), - cancellationToken: ct) - } - , - cancellationToken: Unhandled parameter: ct)) }, - selector: (Object[] results) => (Person)results[0])), - cancellationToken: Unhandled parameter: queryContext.CancellationToken)), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 1 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (5ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (5ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 121.247ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 121.247ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 140.619ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 140.619ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT 1 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT 1 -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/888?include=owner.name -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/888?include=owner.name -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.PatchAsync (JsonApiDotNetCoreExample)' with id 'b46d9cb1-c51a-434d-9ecd-e4dd6d0d699f' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.DeleteAsync (JsonApiDotNetCoreExample)' with id '2deec43b-6907-4519-a36a-21a2dbd0b0f6' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) -fail: JsonApiDotNetCore.Middleware.JsonApiExceptionFilter[0] - An unhandled exception occurred during the request -JsonApiDotNetCore.Internal.JsonApiException: Deeply nested relationships are not supported - at JsonApiDotNetCore.Services.QueryParser.ParseIncludedRelationships(String value) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/QueryParser.cs:line 176 - at JsonApiDotNetCore.Services.QueryParser.Parse(IQueryCollection query) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/QueryParser.cs:line 57 - at JsonApiDotNetCore.Services.JsonApiContext.ApplyContext[T](Object controller) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/JsonApiContext.cs:line 70 - at JsonApiDotNetCore.Controllers.BaseJsonApiController`2..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs:line 56 - at JsonApiDotNetCore.Controllers.JsonApiController`2..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 40 - at JsonApiDotNetCore.Controllers.JsonApiController`1..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 17 - at JsonApiDotNetCoreExample.Controllers.PeopleController..ctor(IJsonApiContext jsonApiContext, IResourceService`1 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs:line 14 - at lambda_method(Closure , IServiceProvider , Object[] ) - at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(ControllerContext controllerContext) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() -fail: JsonApiDotNetCore.Middleware.JsonApiExceptionFilter[0] - An unhandled exception occurred during the request -JsonApiDotNetCore.Internal.JsonApiException: Deeply nested relationships are not supported - at JsonApiDotNetCore.Services.QueryParser.ParseIncludedRelationships(String value) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/QueryParser.cs:line 176 - at JsonApiDotNetCore.Services.QueryParser.Parse(IQueryCollection query) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/QueryParser.cs:line 57 - at JsonApiDotNetCore.Services.JsonApiContext.ApplyContext[T](Object controller) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Services/JsonApiContext.cs:line 70 - at JsonApiDotNetCore.Controllers.BaseJsonApiController`2..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs:line 56 - at JsonApiDotNetCore.Controllers.JsonApiController`2..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 40 - at JsonApiDotNetCore.Controllers.JsonApiController`1..ctor(IJsonApiContext jsonApiContext, IResourceService`2 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/JsonApiDotNetCore/Controllers/JsonApiController.cs:line 17 - at JsonApiDotNetCoreExample.Controllers.PeopleController..ctor(IJsonApiContext jsonApiContext, IResourceService`1 resourceService, ILoggerFactory loggerFactory) in /Users/jarednance/dev/json-api-dotnet-core/src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs:line 14 - at lambda_method(Closure , IServiceProvider , Object[] ) - at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(ControllerContext controllerContext) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) - at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 5.157ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 5.157ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 25.495ms 400 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 25.495ms 400 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p2='?', @p3='?', @p4='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItemCollections" ("Id", "Name", "OwnerId") - VALUES (@p2, @p3, @p4); -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItemCollections" ("Id", "Name", "OwnerId") - VALUES (@p2, @p3, @p4); -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItemCollections" ("Id", "Name", "OwnerId") - VALUES (@p2, @p3, @p4); -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/892?include=todo-items,todo-collections -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/892?include=todo-items,todo-collections -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.PatchAsync (JsonApiDotNetCoreExample)' with id '3f6bab1b-ca52-4fbd-a8c4-dee3c0df1851' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.DeleteAsync (JsonApiDotNetCoreExample)' with id '0c5a6576-2492-4aff-80c7-4f36a82e8d14' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments (892) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments (892) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]) - .Include("TodoItems") - .Include("TodoItemCollections") - .FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[e].TodoItems' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[e].TodoItemCollections' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - order by (Nullable)EF.Property(?[e]?, "Id") asc - select Task _IncludeAsync( - queryContext: queryContext, - entity: [e], - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included | CancellationToken ct) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Task _AwaitMany(new Func[] - { - () => Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => - from TodoItem e.TodoItems in DbSet - join AnonymousObject _e in - (from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - order by (Nullable)EF.Property(?[e]?, "Id") asc - select new AnonymousObject(new object[]{ (object)EF.Property(?[e]?, "Id") })).Take(1) - on Property([e.TodoItems], "OwnerId") equals (Nullable)object [_e].GetValue(0) - order by object [_e].GetValue(0) asc - select [e.TodoItems], - cancellationToken: ct), - () => Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 1, - navigation: Person.TodoItemCollections, - inverseNavigation: TodoItemCollection.Owner, - targetEntityType: EntityType: TodoItemCollection, - clrCollectionAccessor: ClrICollectionAccessor, TodoItemCollection>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => - from TodoItemCollection e.TodoItemCollections in DbSet - join AnonymousObject _e in - (from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - order by (Nullable)EF.Property(?[e]?, "Id") asc - select new AnonymousObject(new object[]{ (object)EF.Property(?[e]?, "Id") })).Take(1) - on Property([e.TodoItemCollections], "OwnerId") equals (Nullable)object [_e].GetValue(0) - order by object [_e].GetValue(0) asc - select [e.TodoItemCollections], - cancellationToken: ct) - }) - } - , - cancellationToken: ct).Result).FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task FirstOrDefault( - source: IAsyncEnumerable _SelectAsync( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 1, - shaper: BufferedEntityShaper), - selector: (Person e | CancellationToken ct) => Task _ExecuteAsync( - taskFactories: new Func>[]{ () => Task _ToObjectTask(Task _IncludeAsync( - queryContext: queryContext, - entity: e, - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included | CancellationToken ct) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Task _AwaitMany(new Func[] - { - () => Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id", - shaper: BufferedEntityShaper), - cancellationToken: ct), - () => Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 1, - navigation: Person.TodoItemCollections, - inverseNavigation: TodoItemCollection.Owner, - targetEntityType: EntityType: TodoItemCollection, - clrCollectionAccessor: ClrICollectionAccessor, TodoItemCollection>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e.TodoItemCollections"."Id", "e.TodoItemCollections"."Name", "e.TodoItemCollections"."OwnerId" - FROM "TodoItemCollections" AS "e.TodoItemCollections" - INNER JOIN ( - SELECT "e1"."Id" - FROM "People" AS "e1" - WHERE "e1"."Id" = @__id_0 - ORDER BY "e1"."Id" - LIMIT 1 - ) AS "t0" ON "e.TodoItemCollections"."OwnerId" = "t0"."Id" - ORDER BY "t0"."Id", - shaper: BufferedEntityShaper), - cancellationToken: ct) - }) - } - , - cancellationToken: Unhandled parameter: ct)) }, - selector: (Object[] results) => (Person)results[0])), - cancellationToken: Unhandled parameter: queryContext.CancellationToken)), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (9ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (9ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - ORDER BY "e"."Id" - LIMIT 1 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItems"."Id", "e.TodoItems"."AchievedDate", "e.TodoItems"."AssigneeId", "e.TodoItems"."CollectionId", "e.TodoItems"."CreatedDate", "e.TodoItems"."Description", "e.TodoItems"."GuidProperty", "e.TodoItems"."Ordinal", "e.TodoItems"."OwnerId" - FROM "TodoItems" AS "e.TodoItems" - INNER JOIN ( - SELECT "e0"."Id" - FROM "People" AS "e0" - WHERE "e0"."Id" = @__id_0 - ORDER BY "e0"."Id" - LIMIT 1 - ) AS "t" ON "e.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItemCollections"."Id", "e.TodoItemCollections"."Name", "e.TodoItemCollections"."OwnerId" - FROM "TodoItemCollections" AS "e.TodoItemCollections" - INNER JOIN ( - SELECT "e1"."Id" - FROM "People" AS "e1" - WHERE "e1"."Id" = @__id_0 - ORDER BY "e1"."Id" - LIMIT 1 - ) AS "t0" ON "e.TodoItemCollections"."OwnerId" = "t0"."Id" - ORDER BY "t0"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItemCollections"."Id", "e.TodoItemCollections"."Name", "e.TodoItemCollections"."OwnerId" - FROM "TodoItemCollections" AS "e.TodoItemCollections" - INNER JOIN ( - SELECT "e1"."Id" - FROM "People" AS "e1" - WHERE "e1"."Id" = @__id_0 - ORDER BY "e1"."Id" - LIMIT 1 - ) AS "t0" ON "e.TodoItemCollections"."OwnerId" = "t0"."Id" - ORDER BY "t0"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e.TodoItemCollections"."Id", "e.TodoItemCollections"."Name", "e.TodoItemCollections"."OwnerId" - FROM "TodoItemCollections" AS "e.TodoItemCollections" - INNER JOIN ( - SELECT "e1"."Id" - FROM "People" AS "e1" - WHERE "e1"."Id" = @__id_0 - ORDER BY "e1"."Id" - LIMIT 1 - ) AS "t0" ON "e.TodoItemCollections"."OwnerId" = "t0"."Id" - ORDER BY "t0"."Id" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 129.696ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 129.696ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 149.864ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 149.864ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p0; - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p1, @p2) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; - DELETE FROM "TodoItems" - WHERE "Id" = @p6; - DELETE FROM "TodoItems" - WHERE "Id" = @p7; - DELETE FROM "TodoItems" - WHERE "Id" = @p8; - DELETE FROM "TodoItems" - WHERE "Id" = @p9; - DELETE FROM "TodoItems" - WHERE "Id" = @p10; - DELETE FROM "TodoItems" - WHERE "Id" = @p11; - DELETE FROM "TodoItems" - WHERE "Id" = @p12; - DELETE FROM "TodoItems" - WHERE "Id" = @p13; - DELETE FROM "TodoItems" - WHERE "Id" = @p14; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p0; - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p1, @p2) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; - DELETE FROM "TodoItems" - WHERE "Id" = @p6; - DELETE FROM "TodoItems" - WHERE "Id" = @p7; - DELETE FROM "TodoItems" - WHERE "Id" = @p8; - DELETE FROM "TodoItems" - WHERE "Id" = @p9; - DELETE FROM "TodoItems" - WHERE "Id" = @p10; - DELETE FROM "TodoItems" - WHERE "Id" = @p11; - DELETE FROM "TodoItems" - WHERE "Id" = @p12; - DELETE FROM "TodoItems" - WHERE "Id" = @p13; - DELETE FROM "TodoItems" - WHERE "Id" = @p14; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p0; - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p1, @p2) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; - DELETE FROM "TodoItems" - WHERE "Id" = @p6; - DELETE FROM "TodoItems" - WHERE "Id" = @p7; - DELETE FROM "TodoItems" - WHERE "Id" = @p8; - DELETE FROM "TodoItems" - WHERE "Id" = @p9; - DELETE FROM "TodoItems" - WHERE "Id" = @p10; - DELETE FROM "TodoItems" - WHERE "Id" = @p11; - DELETE FROM "TodoItems" - WHERE "Id" = @p12; - DELETE FROM "TodoItems" - WHERE "Id" = @p13; - DELETE FROM "TodoItems" - WHERE "Id" = @p14; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p15; - DELETE FROM "People" - WHERE "Id" = @p16; - DELETE FROM "People" - WHERE "Id" = @p17; - DELETE FROM "People" - WHERE "Id" = @p18; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p15; - DELETE FROM "People" - WHERE "Id" = @p16; - DELETE FROM "People" - WHERE "Id" = @p17; - DELETE FROM "People" - WHERE "Id" = @p18; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p15; - DELETE FROM "People" - WHERE "Id" = @p16; - DELETE FROM "People" - WHERE "Id" = @p17; - DELETE FROM "People" - WHERE "Id" = @p18; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2633?include=owner&include=assignee -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2633?include=owner&include=assignee -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '49eb2ce9-b9a4-4b60-8f0d-9dc16928dad7' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '0d5cc266-1ec4-468a-84c2-4323d7e9b49c' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2633) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]) - .Include("Owner") - .Include("Assignee") - .FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[e].Owner' -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[e].Assignee' -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2633) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - join Person e.Assignee in DbSet - on Property([e], "AssigneeId") equals (Nullable)Property([e.Assignee], "Id") into e.Assignee_group - from Person e.Assignee in - (from Person e.Assignee_groupItem in [e.Assignee_group] - select [e.Assignee_groupItem]).DefaultIfEmpty() - join Person e.Owner in DbSet - on Property([e], "OwnerId") equals (Nullable)Property([e.Owner], "Id") into e.Owner_group - from Person e.Owner in - (from Person e.Owner_groupItem in [e.Owner_group] - select [e.Owner_groupItem]).DefaultIfEmpty() - where bool [e].Id.Equals((object)__id_0) - select TodoItem _Include( - queryContext: queryContext, - entity: [e], - included: new object[] - { - [e.Owner], - [e.Assignee] - }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - return !(bool ReferenceEquals(included[1], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[1], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Assignee, - entity: entity, - value: included[1]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.AssignedTodoItems, - entity: included[1], - value: entity) - } - : default(Void) - } - )).FirstOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task FirstOrDefault( - source: IAsyncEnumerable _Select( - source: IAsyncEnumerable, Person>> _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Assignee"."Id", "e.Assignee"."FirstName", "e.Assignee"."LastName", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Assignee" ON "e"."AssigneeId" = "e.Assignee"."Id" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1, - shaper: TypedCompositeShaper, TodoItem, BufferedOffsetEntityShaper, Person, TransparentIdentifier>, TransparentIdentifier, BufferedOffsetEntityShaper, Person, TransparentIdentifier, Person>>), - selector: (TransparentIdentifier, Person> t3) => TodoItem _Include( - queryContext: queryContext, - entity: t3.Outer.Outer, - included: new object[] - { - t3.Inner, - t3.Outer.Inner - }, - fixup: (QueryContext queryContext | TodoItem entity | Object[] included) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: TodoItem) - !(bool ReferenceEquals(included[0], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[0], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Owner, - entity: entity, - value: included[0]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.TodoItems, - entity: included[0], - value: entity) - } - : default(Void) - return !(bool ReferenceEquals(included[1], null)) ? - { - Void queryContext.QueryBuffer.StartTracking( - entity: included[1], - entityType: EntityType: Person) - Void SetRelationshipSnapshotValue( - stateManager: queryContext.StateManager, - navigation: TodoItem.Assignee, - entity: entity, - value: included[1]) - return Void AddToCollectionSnapshot( - stateManager: queryContext.StateManager, - navigation: Person.AssignedTodoItems, - entity: included[1], - value: entity) - } - : default(Void) - } - )), - cancellationToken: Unhandled parameter: queryContext.CancellationToken)), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Assignee"."Id", "e.Assignee"."FirstName", "e.Assignee"."LastName", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Assignee" ON "e"."AssigneeId" = "e.Assignee"."Id" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (15ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Assignee"."Id", "e.Assignee"."FirstName", "e.Assignee"."LastName", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Assignee" ON "e"."AssigneeId" = "e.Assignee"."Id" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (15ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId", "e.Assignee"."Id", "e.Assignee"."FirstName", "e.Assignee"."LastName", "e.Owner"."Id", "e.Owner"."FirstName", "e.Owner"."LastName" - FROM "TodoItems" AS "e" - LEFT JOIN "People" AS "e.Assignee" ON "e"."AssigneeId" = "e.Assignee"."Id" - LEFT JOIN "People" AS "e.Owner" ON "e"."OwnerId" = "e.Owner"."Id" - WHERE "e"."Id" = @__id_0 - LIMIT 1 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 111.286ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 111.286ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 131.712ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 131.712ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p3; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p3; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "People" - WHERE "Id" = @p3; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people?include=todo-items -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people?include=todo-items -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.PostAsync (JsonApiDotNetCoreExample)' with id '1eaf2dd2-1dd5-46db-ba6f-88148a37f5ec' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _2 in DbSet - select [_2]) - .Include("TodoItems") - .Count()' -warn: Microsoft.EntityFrameworkCore.Query[10106] - The Include operation for navigation '[_2].TodoItems' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _2 in DbSet - select [_2]).Count()' -warn: Microsoft.EntityFrameworkCore.Query[10106] - The Include operation for navigation '[_2].TodoItems' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information. -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "People" AS "p"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _3 in DbSet - select [_3]) - .Include("TodoItems") - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from Person _3 in DbSet select [_3]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from Person _3 in DbSet select [_3]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10105] - Including navigation: '[_3].TodoItems' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _3 in DbSet - order by (Nullable)EF.Property(?[_3]?, "Id") asc - select Task _IncludeAsync( - queryContext: queryContext, - entity: [_3], - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included | CancellationToken ct) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => - from TodoItem p.TodoItems in DbSet - join AnonymousObject __3 in - (from Person _3 in DbSet - order by (Nullable)EF.Property(?[_3]?, "Id") asc - select new AnonymousObject(new object[]{ (object)EF.Property(?[_3]?, "Id") })) - .Skip(__p_0) - .Take(__p_1) - on Property([p.TodoItems], "OwnerId") equals (Nullable)object [__3].GetValue(0) - order by object [__3].GetValue(0) asc - select [p.TodoItems], - cancellationToken: ct) - } - , - cancellationToken: ct).Result) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _SelectAsync( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - ORDER BY "p"."Id" - LIMIT @__p_1 OFFSET @__p_0, - shaper: BufferedEntityShaper), - selector: (Person _3 | CancellationToken ct) => Task _ExecuteAsync( - taskFactories: new Func>[]{ () => Task _ToObjectTask(Task _IncludeAsync( - queryContext: queryContext, - entity: _3, - included: new object[]{ }, - fixup: (QueryContext queryContext | Person entity | Object[] included | CancellationToken ct) => - { - Void queryContext.QueryBuffer.StartTracking( - entity: entity, - entityType: EntityType: Person) - return Task queryContext.QueryBuffer.IncludeCollectionAsync( - includeId: 0, - navigation: Person.TodoItems, - inverseNavigation: TodoItem.Owner, - targetEntityType: EntityType: TodoItem, - clrCollectionAccessor: ClrICollectionAccessor, TodoItem>, - inverseClrPropertySetter: ClrPropertySetter, - tracking: True, - instance: entity, - valuesFactory: (Func>)() => IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p.TodoItems"."Id", "p.TodoItems"."AchievedDate", "p.TodoItems"."AssigneeId", "p.TodoItems"."CollectionId", "p.TodoItems"."CreatedDate", "p.TodoItems"."Description", "p.TodoItems"."GuidProperty", "p.TodoItems"."Ordinal", "p.TodoItems"."OwnerId" - FROM "TodoItems" AS "p.TodoItems" - INNER JOIN ( - SELECT "p0"."Id" - FROM "People" AS "p0" - ORDER BY "p0"."Id" - LIMIT @__p_1 OFFSET @__p_0 - ) AS "t" ON "p.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id", - shaper: BufferedEntityShaper), - cancellationToken: ct) - } - , - cancellationToken: Unhandled parameter: ct)) }, - selector: (Object[] results) => (Person)results[0])), - queryContext: Unhandled parameter: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: Unhandled parameter: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - ORDER BY "p"."Id" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - ORDER BY "p"."Id" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - ORDER BY "p"."Id" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p.TodoItems"."Id", "p.TodoItems"."AchievedDate", "p.TodoItems"."AssigneeId", "p.TodoItems"."CollectionId", "p.TodoItems"."CreatedDate", "p.TodoItems"."Description", "p.TodoItems"."GuidProperty", "p.TodoItems"."Ordinal", "p.TodoItems"."OwnerId" - FROM "TodoItems" AS "p.TodoItems" - INNER JOIN ( - SELECT "p0"."Id" - FROM "People" AS "p0" - ORDER BY "p0"."Id" - LIMIT @__p_1 OFFSET @__p_0 - ) AS "t" ON "p.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p.TodoItems"."Id", "p.TodoItems"."AchievedDate", "p.TodoItems"."AssigneeId", "p.TodoItems"."CollectionId", "p.TodoItems"."CreatedDate", "p.TodoItems"."Description", "p.TodoItems"."GuidProperty", "p.TodoItems"."Ordinal", "p.TodoItems"."OwnerId" - FROM "TodoItems" AS "p.TodoItems" - INNER JOIN ( - SELECT "p0"."Id" - FROM "People" AS "p0" - ORDER BY "p0"."Id" - LIMIT @__p_1 OFFSET @__p_0 - ) AS "t" ON "p.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p.TodoItems"."Id", "p.TodoItems"."AchievedDate", "p.TodoItems"."AssigneeId", "p.TodoItems"."CollectionId", "p.TodoItems"."CreatedDate", "p.TodoItems"."Description", "p.TodoItems"."GuidProperty", "p.TodoItems"."Ordinal", "p.TodoItems"."OwnerId" - FROM "TodoItems" AS "p.TodoItems" - INNER JOIN ( - SELECT "p0"."Id" - FROM "People" AS "p0" - ORDER BY "p0"."Id" - LIMIT @__p_1 OFFSET @__p_0 - ) AS "t" ON "p.TodoItems"."OwnerId" = "t"."Id" - ORDER BY "t"."Id" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 91.629ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 91.629ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 111.506ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 111.506ms 200 application/vnd.api+json -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.PostAsync (JsonApiDotNetCoreExample)' with id '101783b6-922d-478e-91c4-973fb0212ed5' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "People" AS "p"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (26ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (26ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from Person _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from Person _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 76.867ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 76.867ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 97.983ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 97.983ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id", "CreatedDate"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExampleTests -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample)' with id '66278d25-d1b4-4a31-8edc-371fd1e02b91' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (9ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (9ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 51.071ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 51.071ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 73.957ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 73.957ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?', @p38='?', @p39='?', @p40='?', @p41='?', @p42='?', @p43='?', @p44='?', @p45='?', @p46='?', @p47='?', @p48='?', @p49='?', @p50='?', @p51='?', @p52='?', @p53='?', @p54='?', @p55='?', @p56='?', @p57='?', @p58='?', @p59='?', @p60='?', @p61='?', @p62='?', @p63='?', @p64='?', @p65='?', @p66='?', @p67='?', @p68='?', @p69='?', @p70='?', @p71='?', @p72='?', @p73='?', @p74='?', @p75='?', @p76='?', @p77='?', @p78='?', @p79='?', @p80='?', @p81='?', @p82='?', @p83='?', @p84='?', @p85='?', @p86='?', @p87='?', @p88='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p25, @p26, @p27, @p28, @p29, @p30, @p31, @p32) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p33, @p34, @p35, @p36, @p37, @p38, @p39, @p40) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p41, @p42, @p43, @p44, @p45, @p46, @p47, @p48) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p49, @p50, @p51, @p52, @p53, @p54, @p55, @p56) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p57, @p58, @p59, @p60, @p61, @p62, @p63, @p64) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p65, @p66, @p67, @p68, @p69, @p70, @p71, @p72) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p73, @p74, @p75, @p76, @p77, @p78, @p79, @p80) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p81, @p82, @p83, @p84, @p85, @p86, @p87, @p88) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?', @p38='?', @p39='?', @p40='?', @p41='?', @p42='?', @p43='?', @p44='?', @p45='?', @p46='?', @p47='?', @p48='?', @p49='?', @p50='?', @p51='?', @p52='?', @p53='?', @p54='?', @p55='?', @p56='?', @p57='?', @p58='?', @p59='?', @p60='?', @p61='?', @p62='?', @p63='?', @p64='?', @p65='?', @p66='?', @p67='?', @p68='?', @p69='?', @p70='?', @p71='?', @p72='?', @p73='?', @p74='?', @p75='?', @p76='?', @p77='?', @p78='?', @p79='?', @p80='?', @p81='?', @p82='?', @p83='?', @p84='?', @p85='?', @p86='?', @p87='?', @p88='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p25, @p26, @p27, @p28, @p29, @p30, @p31, @p32) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p33, @p34, @p35, @p36, @p37, @p38, @p39, @p40) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p41, @p42, @p43, @p44, @p45, @p46, @p47, @p48) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p49, @p50, @p51, @p52, @p53, @p54, @p55, @p56) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p57, @p58, @p59, @p60, @p61, @p62, @p63, @p64) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p65, @p66, @p67, @p68, @p69, @p70, @p71, @p72) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p73, @p74, @p75, @p76, @p77, @p78, @p79, @p80) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p81, @p82, @p83, @p84, @p85, @p86, @p87, @p88) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?', @p38='?', @p39='?', @p40='?', @p41='?', @p42='?', @p43='?', @p44='?', @p45='?', @p46='?', @p47='?', @p48='?', @p49='?', @p50='?', @p51='?', @p52='?', @p53='?', @p54='?', @p55='?', @p56='?', @p57='?', @p58='?', @p59='?', @p60='?', @p61='?', @p62='?', @p63='?', @p64='?', @p65='?', @p66='?', @p67='?', @p68='?', @p69='?', @p70='?', @p71='?', @p72='?', @p73='?', @p74='?', @p75='?', @p76='?', @p77='?', @p78='?', @p79='?', @p80='?', @p81='?', @p82='?', @p83='?', @p84='?', @p85='?', @p86='?', @p87='?', @p88='?'], CommandType='Text', CommandTimeout='30'] - DELETE FROM "TodoItems" - WHERE "Id" = @p0; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p25, @p26, @p27, @p28, @p29, @p30, @p31, @p32) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p33, @p34, @p35, @p36, @p37, @p38, @p39, @p40) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p41, @p42, @p43, @p44, @p45, @p46, @p47, @p48) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p49, @p50, @p51, @p52, @p53, @p54, @p55, @p56) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p57, @p58, @p59, @p60, @p61, @p62, @p63, @p64) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p65, @p66, @p67, @p68, @p69, @p70, @p71, @p72) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p73, @p74, @p75, @p76, @p77, @p78, @p79, @p80) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p81, @p82, @p83, @p84, @p85, @p86, @p87, @p88) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?page[number]=2 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?page[number]=2 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample)' with id '29149bf5-e575-4822-ae33-beb5fce2ffc6' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 2 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 51.095ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 70.672ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 51.095ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 70.672ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _1 in DbSet - select [_1]).Last()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _1 in DbSet - select [_1]).Last()' -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'Last()' could not be translated and will be evaluated locally. -warn: Microsoft.EntityFrameworkCore.Query[20500] - The LINQ expression 'Last()' could not be translated and will be evaluated locally. -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _ToSequence(Person Last(IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p", - shaper: UnbufferedEntityShaper))), - queryContext: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/894 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people/894 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.PatchAsync (JsonApiDotNetCoreExample)' with id '1d8d8ad3-300b-4985-9917-0527d2f32c84' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.DeleteAsync (JsonApiDotNetCoreExample)' with id '110246c9-516c-4d15-b9ac-04e6d266b51b' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments (894) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments (894) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: UnbufferedEntityShaper), - cancellationToken: queryContext.CancellationToken)), - queryContext: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."FirstName", "e"."LastName" - FROM "People" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 51.526ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 72.584ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 51.526ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 72.584ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2647 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2647 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '5aeb7733-aac0-4b6a-a5b0-622a10aef310' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '3b020693-8812-48be-bf1b-028a704a3f50' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2647) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2647) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: UnbufferedEntityShaper), - cancellationToken: queryContext.CancellationToken)), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 43.935ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 43.935ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 66.007ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 66.007ms 200 application/vnd.api+json -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.PostAsync (JsonApiDotNetCoreExample)' with id '53236a0e-5a42-4405-8697-b363307b0742' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "People" AS "p"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from Person _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from Person _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 53.752ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 53.752ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 75.703ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 75.703ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2648 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2648 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id 'bc9f4094-da3f-4b54-a4b5-746a2be25e55' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '61e99f52-a4c3-40bd-92cc-955e50e086d9' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2648) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2648) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: UnbufferedEntityShaper), - cancellationToken: queryContext.CancellationToken)), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 43.28ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 43.28ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 67.828ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 67.828ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id", "CreatedDate"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/custom/route/todo-items/2649 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/custom/route/todo-items/2649 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'custom/route/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.PatchAsync (JsonApiDotNetCoreExample)' with id '81b735cc-1698-4dd5-a086-6eda734699c0' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.DeleteAsync (JsonApiDotNetCoreExample)' with id '2d84b987-9823-450c-982d-b63abe1024f7' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) with arguments (2649) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) with arguments (2649) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: UnbufferedEntityShaper), - cancellationToken: queryContext.CancellationToken)), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) in 39.12ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) in 39.12ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 60.789ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 60.789ms 200 application/vnd.api+json -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/testValues -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/testValues -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'TestValues'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TestValuesController.Get (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TestValuesController.Get (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TestValuesController.Get (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TestValuesController.Get (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: JsonApiDotNetCore.Serialization.JsonApiSerializer[0] - Response was not a JSONAPI entity. Serializing as plain JSON. -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TestValuesController.Get (JsonApiDotNetCoreExample) in 1.407ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 32.217ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TestValuesController.Get (JsonApiDotNetCoreExample) in 1.407ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 32.217ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (6ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (6ms) [Parameters=[@p0='?', @p1='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id", "CreatedDate"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/custom/route/todo-items/2650 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/custom/route/todo-items/2650 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'custom/route/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.PatchAsync (JsonApiDotNetCoreExample)' with id '49ee8a92-89ed-4f38-906c-31ab8083271c' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.DeleteAsync (JsonApiDotNetCoreExample)' with id '744dcbe5-efca-479f-bb8f-d7e1c104d561' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) with arguments (2650) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem e in DbSet - where bool [e].Id.Equals((object)__id_0) - select [e]).SingleOrDefault()' -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) with arguments (2650) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ToSequence(Task SingleOrDefault( - source: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2, - shaper: UnbufferedEntityShaper), - cancellationToken: queryContext.CancellationToken)), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) in 43.411ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) in 43.411ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 65.45ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 65.45ms 200 application/vnd.api+json -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/custom/route/todo-items -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/custom/route/todo-items -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'custom/route/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.PostAsync (JsonApiDotNetCoreExample)' with id '38524268-32e2-49e7-872f-c4f51a0ad8cc' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) in 54.945ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsCustomController.GetAsync (JsonApiDotNetCoreExample) in 54.945ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 69.838ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 69.838ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2651?omitNullValuedAttributes=true -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2651?omitNullValuedAttributes=true -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2651) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2651) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.765ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.765ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 3.533ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 3.533ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2652?omitNullValuedAttributes=false -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2652?omitNullValuedAttributes=false -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2652) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2652) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 3.109ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 3.109ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 3.54ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 3.54ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2653 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2653 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2653) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2653) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.247ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.247ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.645ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.645ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2654 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2654 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2654) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2654) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 3.771ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 3.771ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 4.491ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 4.491ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2655?omitNullValuedAttributes= -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2655?omitNullValuedAttributes= -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2655) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2655) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.271ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.271ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.444ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.444ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2656?omitNullValuedAttributes=false -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2656?omitNullValuedAttributes=false -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2656) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2656) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 7.526ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 7.526ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 7.947ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 7.947ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2657?omitNullValuedAttributes=true -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2657?omitNullValuedAttributes=true -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2657) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2657) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.605ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.605ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.794ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.794ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2658?omitNullValuedAttributes=true -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2658?omitNullValuedAttributes=true -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2658) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2658) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.479ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.479ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.849ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.849ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2659?omitNullValuedAttributes=foo -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2659?omitNullValuedAttributes=foo -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2659) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2659) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.266ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.266ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.441ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.441ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2660?omitNullValuedAttributes=foo -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2660?omitNullValuedAttributes=foo -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2660) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2660) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.248ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.248ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 3.239ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 3.239ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2661?omitNullValuedAttributes=foo -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2661?omitNullValuedAttributes=foo -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2661) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2661) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.492ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.683ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.492ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.683ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2662?omitNullValuedAttributes=false -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2662?omitNullValuedAttributes=false -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2662) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2662) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.181ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.181ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.539ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.539ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2663?omitNullValuedAttributes=true -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2663?omitNullValuedAttributes=true -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2663) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2663) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.411ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.411ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.782ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.782ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (5ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (5ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2664?omitNullValuedAttributes=false -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2664?omitNullValuedAttributes=false -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2664) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2664) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.755ms -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.951ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.755ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.951ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2665?omitNullValuedAttributes=foo -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2665?omitNullValuedAttributes=foo -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2665) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2665) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.481ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.481ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.66ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 2.66ms 200 application/vnd.api+json -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2666?omitNullValuedAttributes= -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items/2666?omitNullValuedAttributes= -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items/{id}'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PatchAsync (JsonApiDotNetCoreExample)' with id '1ee61db8-daf4-41f3-90d2-1cf3db87d448' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.DeleteAsync (JsonApiDotNetCoreExample)' with id '75ab2821-99d0-4a3f-9dbb-3747ee7b296e' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2666) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments (2666) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "e"."Id", "e"."AchievedDate", "e"."AssigneeId", "e"."CollectionId", "e"."CreatedDate", "e"."Description", "e"."GuidProperty", "e"."Ordinal", "e"."OwnerId" - FROM "TodoItems" AS "e" - WHERE "e"."Id" = @__id_0 - LIMIT 2 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.802ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 2.802ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 3.245ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 3.245ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p2, @p3, @p4, @p5, @p6, @p7, @p8) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p9, @p10, @p11, @p12, @p13, @p14, @p15) - RETURNING "Id", "CreatedDate"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p9, @p10, @p11, @p12, @p13, @p14, @p15) - RETURNING "Id", "CreatedDate"; -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?include=owner -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?include=owner -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -warn: Microsoft.EntityFrameworkCore.Query[10106] - The Include operation for navigation '[todoItem].Owner' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information. -warn: Microsoft.EntityFrameworkCore.Query[10106] - The Include operation for navigation '[todoItem].Owner' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@___authService_CurrentUserId_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "todoItem" - WHERE "todoItem"."OwnerId" = @___authService_CurrentUserId_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@___authService_CurrentUserId_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "todoItem" - WHERE "todoItem"."OwnerId" = @___authService_CurrentUserId_0 -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem todoItem in DbSet where [todoItem].OwnerId == (Nullable)___authService...' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem todoItem in DbSet where [todoItem].OwnerId == (Nullable)___authService...' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@___authService_CurrentUserId_0='?', @__p_2='?', @__p_1='?'], CommandType='Text', CommandTimeout='30'] - SELECT "todoItem"."Id", "todoItem"."AchievedDate", "todoItem"."AssigneeId", "todoItem"."CollectionId", "todoItem"."CreatedDate", "todoItem"."Description", "todoItem"."GuidProperty", "todoItem"."Ordinal", "todoItem"."OwnerId", "todoItem.Owner"."Id", "todoItem.Owner"."FirstName", "todoItem.Owner"."LastName" - FROM "TodoItems" AS "todoItem" - LEFT JOIN "People" AS "todoItem.Owner" ON "todoItem"."OwnerId" = "todoItem.Owner"."Id" - WHERE "todoItem"."OwnerId" = @___authService_CurrentUserId_0 - LIMIT @__p_2 OFFSET @__p_1 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@___authService_CurrentUserId_0='?', @__p_2='?', @__p_1='?'], CommandType='Text', CommandTimeout='30'] - SELECT "todoItem"."Id", "todoItem"."AchievedDate", "todoItem"."AssigneeId", "todoItem"."CollectionId", "todoItem"."CreatedDate", "todoItem"."Description", "todoItem"."GuidProperty", "todoItem"."Ordinal", "todoItem"."OwnerId", "todoItem.Owner"."Id", "todoItem.Owner"."FirstName", "todoItem.Owner"."LastName" - FROM "TodoItems" AS "todoItem" - LEFT JOIN "People" AS "todoItem.Owner" ON "todoItem"."OwnerId" = "todoItem.Owner"."Id" - WHERE "todoItem"."OwnerId" = @___authService_CurrentUserId_0 - LIMIT @__p_2 OFFSET @__p_1 -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 48.653ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 48.653ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 74.804ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 74.804ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExampleTests -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/people -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/people'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.PeopleController.PostAsync (JsonApiDotNetCoreExample)' with id '1ded46dd-748a-4bb3-b33b-d64481e8f7d0' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "People" AS "p"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "People" AS "p" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 5 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from Person _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from Person _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from Person _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from Person _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: Person }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "p"."Id", "p"."FirstName", "p"."LastName" - FROM "People" AS "p" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 54.361ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 78.817ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.PeopleController.GetAsync (JsonApiDotNetCoreExample) in 54.361ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 78.817ms 200 application/vnd.api+json -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[5] - Hosting shutdown -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p24, @p25, @p26, @p27, @p28, @p29, @p30, @p31) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p24, @p25, @p26, @p27, @p28, @p29, @p30, @p31) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p24, @p25, @p26, @p27, @p28, @p29, @p30, @p31) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?page[size]=2 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?page[size]=2 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample)' with id '52af8b31-a0b5-47dc-9061-17639045ff8f' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (10ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 0 with 2 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 38.24ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 38.24ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 64.502ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 64.502ms 200 application/vnd.api+json -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[5] - Hosting shutdown -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - 'from TodoItem _0 in DbSet - select [_0]' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - 'from TodoItem _0 in DbSet - select [_0]' -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t", - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?', @p38='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; - DELETE FROM "TodoItems" - WHERE "Id" = @p6; - DELETE FROM "TodoItems" - WHERE "Id" = @p7; - DELETE FROM "TodoItems" - WHERE "Id" = @p8; - DELETE FROM "TodoItems" - WHERE "Id" = @p9; - DELETE FROM "TodoItems" - WHERE "Id" = @p10; - DELETE FROM "TodoItems" - WHERE "Id" = @p11; - DELETE FROM "TodoItems" - WHERE "Id" = @p12; - DELETE FROM "TodoItems" - WHERE "Id" = @p13; - DELETE FROM "TodoItems" - WHERE "Id" = @p14; - DELETE FROM "TodoItems" - WHERE "Id" = @p15; - DELETE FROM "TodoItems" - WHERE "Id" = @p16; - DELETE FROM "TodoItems" - WHERE "Id" = @p17; - DELETE FROM "TodoItems" - WHERE "Id" = @p18; - DELETE FROM "TodoItems" - WHERE "Id" = @p19; - DELETE FROM "TodoItems" - WHERE "Id" = @p20; - DELETE FROM "TodoItems" - WHERE "Id" = @p21; - DELETE FROM "TodoItems" - WHERE "Id" = @p22; - DELETE FROM "TodoItems" - WHERE "Id" = @p23; - DELETE FROM "TodoItems" - WHERE "Id" = @p24; - DELETE FROM "TodoItems" - WHERE "Id" = @p25; - DELETE FROM "TodoItems" - WHERE "Id" = @p26; - DELETE FROM "TodoItems" - WHERE "Id" = @p27; - DELETE FROM "TodoItems" - WHERE "Id" = @p28; - DELETE FROM "TodoItems" - WHERE "Id" = @p29; - DELETE FROM "TodoItems" - WHERE "Id" = @p30; - DELETE FROM "TodoItems" - WHERE "Id" = @p31; - DELETE FROM "TodoItems" - WHERE "Id" = @p32; - DELETE FROM "TodoItems" - WHERE "Id" = @p33; - DELETE FROM "TodoItems" - WHERE "Id" = @p34; - DELETE FROM "TodoItems" - WHERE "Id" = @p35; - DELETE FROM "TodoItems" - WHERE "Id" = @p36; - DELETE FROM "TodoItems" - WHERE "Id" = @p37; - DELETE FROM "TodoItems" - WHERE "Id" = @p38; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?', @p38='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; - DELETE FROM "TodoItems" - WHERE "Id" = @p6; - DELETE FROM "TodoItems" - WHERE "Id" = @p7; - DELETE FROM "TodoItems" - WHERE "Id" = @p8; - DELETE FROM "TodoItems" - WHERE "Id" = @p9; - DELETE FROM "TodoItems" - WHERE "Id" = @p10; - DELETE FROM "TodoItems" - WHERE "Id" = @p11; - DELETE FROM "TodoItems" - WHERE "Id" = @p12; - DELETE FROM "TodoItems" - WHERE "Id" = @p13; - DELETE FROM "TodoItems" - WHERE "Id" = @p14; - DELETE FROM "TodoItems" - WHERE "Id" = @p15; - DELETE FROM "TodoItems" - WHERE "Id" = @p16; - DELETE FROM "TodoItems" - WHERE "Id" = @p17; - DELETE FROM "TodoItems" - WHERE "Id" = @p18; - DELETE FROM "TodoItems" - WHERE "Id" = @p19; - DELETE FROM "TodoItems" - WHERE "Id" = @p20; - DELETE FROM "TodoItems" - WHERE "Id" = @p21; - DELETE FROM "TodoItems" - WHERE "Id" = @p22; - DELETE FROM "TodoItems" - WHERE "Id" = @p23; - DELETE FROM "TodoItems" - WHERE "Id" = @p24; - DELETE FROM "TodoItems" - WHERE "Id" = @p25; - DELETE FROM "TodoItems" - WHERE "Id" = @p26; - DELETE FROM "TodoItems" - WHERE "Id" = @p27; - DELETE FROM "TodoItems" - WHERE "Id" = @p28; - DELETE FROM "TodoItems" - WHERE "Id" = @p29; - DELETE FROM "TodoItems" - WHERE "Id" = @p30; - DELETE FROM "TodoItems" - WHERE "Id" = @p31; - DELETE FROM "TodoItems" - WHERE "Id" = @p32; - DELETE FROM "TodoItems" - WHERE "Id" = @p33; - DELETE FROM "TodoItems" - WHERE "Id" = @p34; - DELETE FROM "TodoItems" - WHERE "Id" = @p35; - DELETE FROM "TodoItems" - WHERE "Id" = @p36; - DELETE FROM "TodoItems" - WHERE "Id" = @p37; - DELETE FROM "TodoItems" - WHERE "Id" = @p38; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (4ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?', @p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?', @p38='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; - DELETE FROM "TodoItems" - WHERE "Id" = @p6; - DELETE FROM "TodoItems" - WHERE "Id" = @p7; - DELETE FROM "TodoItems" - WHERE "Id" = @p8; - DELETE FROM "TodoItems" - WHERE "Id" = @p9; - DELETE FROM "TodoItems" - WHERE "Id" = @p10; - DELETE FROM "TodoItems" - WHERE "Id" = @p11; - DELETE FROM "TodoItems" - WHERE "Id" = @p12; - DELETE FROM "TodoItems" - WHERE "Id" = @p13; - DELETE FROM "TodoItems" - WHERE "Id" = @p14; - DELETE FROM "TodoItems" - WHERE "Id" = @p15; - DELETE FROM "TodoItems" - WHERE "Id" = @p16; - DELETE FROM "TodoItems" - WHERE "Id" = @p17; - DELETE FROM "TodoItems" - WHERE "Id" = @p18; - DELETE FROM "TodoItems" - WHERE "Id" = @p19; - DELETE FROM "TodoItems" - WHERE "Id" = @p20; - DELETE FROM "TodoItems" - WHERE "Id" = @p21; - DELETE FROM "TodoItems" - WHERE "Id" = @p22; - DELETE FROM "TodoItems" - WHERE "Id" = @p23; - DELETE FROM "TodoItems" - WHERE "Id" = @p24; - DELETE FROM "TodoItems" - WHERE "Id" = @p25; - DELETE FROM "TodoItems" - WHERE "Id" = @p26; - DELETE FROM "TodoItems" - WHERE "Id" = @p27; - DELETE FROM "TodoItems" - WHERE "Id" = @p28; - DELETE FROM "TodoItems" - WHERE "Id" = @p29; - DELETE FROM "TodoItems" - WHERE "Id" = @p30; - DELETE FROM "TodoItems" - WHERE "Id" = @p31; - DELETE FROM "TodoItems" - WHERE "Id" = @p32; - DELETE FROM "TodoItems" - WHERE "Id" = @p33; - DELETE FROM "TodoItems" - WHERE "Id" = @p34; - DELETE FROM "TodoItems" - WHERE "Id" = @p35; - DELETE FROM "TodoItems" - WHERE "Id" = @p36; - DELETE FROM "TodoItems" - WHERE "Id" = @p37; - DELETE FROM "TodoItems" - WHERE "Id" = @p38; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p39='?', @p40='?', @p41='?', @p42='?', @p43='?', @p44='?', @p45='?', @p46='?', @p47='?', @p48='?', @p49='?', @p50='?', @p51='?', @p52='?', @p53='?', @p54='?', @p55='?', @p56='?', @p57='?', @p58='?', @p59='?', @p60='?', @p61='?', @p62='?', @p63='?', @p64='?', @p65='?', @p66='?', @p67='?', @p68='?', @p69='?', @p70='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p39, @p40, @p41, @p42, @p43, @p44, @p45, @p46) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p47, @p48, @p49, @p50, @p51, @p52, @p53, @p54) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p55, @p56, @p57, @p58, @p59, @p60, @p61, @p62) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p63, @p64, @p65, @p66, @p67, @p68, @p69, @p70) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p39='?', @p40='?', @p41='?', @p42='?', @p43='?', @p44='?', @p45='?', @p46='?', @p47='?', @p48='?', @p49='?', @p50='?', @p51='?', @p52='?', @p53='?', @p54='?', @p55='?', @p56='?', @p57='?', @p58='?', @p59='?', @p60='?', @p61='?', @p62='?', @p63='?', @p64='?', @p65='?', @p66='?', @p67='?', @p68='?', @p69='?', @p70='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p39, @p40, @p41, @p42, @p43, @p44, @p45, @p46) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p47, @p48, @p49, @p50, @p51, @p52, @p53, @p54) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p55, @p56, @p57, @p58, @p59, @p60, @p61, @p62) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p63, @p64, @p65, @p66, @p67, @p68, @p69, @p70) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[@p39='?', @p40='?', @p41='?', @p42='?', @p43='?', @p44='?', @p45='?', @p46='?', @p47='?', @p48='?', @p49='?', @p50='?', @p51='?', @p52='?', @p53='?', @p54='?', @p55='?', @p56='?', @p57='?', @p58='?', @p59='?', @p60='?', @p61='?', @p62='?', @p63='?', @p64='?', @p65='?', @p66='?', @p67='?', @p68='?', @p69='?', @p70='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p39, @p40, @p41, @p42, @p43, @p44, @p45, @p46) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p47, @p48, @p49, @p50, @p51, @p52, @p53, @p54) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p55, @p56, @p57, @p58, @p59, @p60, @p61, @p62) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p63, @p64, @p65, @p66, @p67, @p68, @p69, @p70) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?page[size]=2&page[number]=1 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?page[size]=2&page[number]=1 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample)' with id '8b075eda-2371-4b4f-8774-1f6d937181b9' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page 1 with 2 entities -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 32.06ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 32.06ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 56.326ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 56.326ms 200 application/vnd.api+json -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[5] - Hosting shutdown -dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] - An 'IServiceProvider' was created for internal use by Entity Framework. -warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] - More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. Consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. -info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] - User profile is available. Using '/Users/jarednance/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-b040dd03-33c5-451e-8f42-3c9dd4e0dfec.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-c8147b66-2deb-407c-9421-1553f0e16d67.xml'. -dbug: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[37] - Reading data from file '/Users/jarednance/.aspnet/DataProtection-Keys/key-f7fa4a09-6d65-426e-a653-cbd2969e5073.xml'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {b040dd03-33c5-451e-8f42-3c9dd4e0dfec}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {c8147b66-2deb-407c-9421-1553f0e16d67}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[18] - Found key {f7fa4a09-6d65-426e-a653-cbd2969e5073}. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[13] - Considering key {c8147b66-2deb-407c-9421-1553f0e16d67} with expiration date 2018-03-03 16:22:35Z as default key. -dbug: Microsoft.AspNetCore.DataProtection.TypeForwardingActivator[0] - Forwarded activator type request from Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60 -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[11] - Using managed symmetric algorithm 'System.Security.Cryptography.Aes'. -dbug: Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ManagedAuthenticatedEncryptorFactory[10] - Using managed keyed hash algorithm 'System.Security.Cryptography.HMACSHA256'. -dbug: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider[2] - Using key {c8147b66-2deb-407c-9421-1553f0e16d67} as the default key. -dbug: Microsoft.AspNetCore.DataProtection.Internal.DataProtectionStartupFilter[0] - Key ring with default key {c8147b66-2deb-407c-9421-1553f0e16d67} was loaded during application startup. -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - - SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END - FROM information_schema.tables - WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3] - Hosting starting -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4] - Hosting started -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0] - Loaded hosting startup assembly JsonApiDotNetCoreExample -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - 'from TodoItem _0 in DbSet - select [_0]' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - 'from TodoItem _0 in DbSet - select [_0]' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IEnumerable _InterceptExceptions( - source: IEnumerable _TrackEntities( - results: IEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t", - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200] - Beginning transaction with isolation level 'ReadCommitted'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "People" ("FirstName", "LastName") - VALUES (@p0, @p1) - RETURNING "Id"; - DELETE FROM "TodoItems" - WHERE "Id" = @p2; - DELETE FROM "TodoItems" - WHERE "Id" = @p3; - DELETE FROM "TodoItems" - WHERE "Id" = @p4; - DELETE FROM "TodoItems" - WHERE "Id" = @p5; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p22, @p23, @p24, @p25, @p26, @p27, @p28, @p29) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p30, @p31, @p32, @p33, @p34, @p35, @p36, @p37) - RETURNING "Id"; -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p22, @p23, @p24, @p25, @p26, @p27, @p28, @p29) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p30, @p31, @p32, @p33, @p34, @p35, @p36, @p37) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@p6='?', @p7='?', @p8='?', @p9='?', @p10='?', @p11='?', @p12='?', @p13='?', @p14='?', @p15='?', @p16='?', @p17='?', @p18='?', @p19='?', @p20='?', @p21='?', @p22='?', @p23='?', @p24='?', @p25='?', @p26='?', @p27='?', @p28='?', @p29='?', @p30='?', @p31='?', @p32='?', @p33='?', @p34='?', @p35='?', @p36='?', @p37='?'], CommandType='Text', CommandTimeout='30'] - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p22, @p23, @p24, @p25, @p26, @p27, @p28, @p29) - RETURNING "Id"; - INSERT INTO "TodoItems" ("AchievedDate", "AssigneeId", "CollectionId", "CreatedDate", "Description", "GuidProperty", "Ordinal", "OwnerId") - VALUES (@p30, @p31, @p32, @p33, @p34, @p35, @p36, @p37) - RETURNING "Id"; -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202] - Committing transaction. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204] - Disposing transaction. -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?page[size]=2&page[number]=-1 -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] - Request starting HTTP/1.1 GET http://localhost/api/v1/todo-items?page[size]=2&page[number]=-1 -dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1] - Request successfully matched the route with name '(null)' and template 'api/v1/todo-items'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2] - Action 'JsonApiDotNetCoreExample.Controllers.TodoItemsController.PostAsync (JsonApiDotNetCoreExample)' with id '0185f6c8-2dbf-416d-abd0-a7a72d063bea' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint' -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] - Executing action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) with arguments ((null)) - ModelState is Valid -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -info: Microsoft.EntityFrameworkCore.Infrastructure[10403] - Entity Framework Core 2.0.1-rtm-125 initialized 'AppDbContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _1 in DbSet - select [_1]).Count()' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _ToSequence(Task GetResult( - valueBuffers: IAsyncEnumerable _Query( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t"), - throwOnNullResult: False, - cancellationToken: queryContext.CancellationToken)), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: JsonApiDotNetCore.Services.EntityResourceService[0] - Applying paging query. Fetching page -1 with 2 entities -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] - SELECT COUNT(*)::INT4 - FROM "TodoItems" AS "t" -dbug: Microsoft.EntityFrameworkCore.Query[10101] - Compiling query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -warn: Microsoft.EntityFrameworkCore.Query[10102] - Query: '(from TodoItem _2 in DbSet select [_2]).Skip(__p_0).Take(__p_1)' uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results. -dbug: Microsoft.EntityFrameworkCore.Query[10104] - Optimized query model: - '(from TodoItem _2 in DbSet - select [_2]) - .Skip(__p_0) - .Take(__p_1)' -dbug: Microsoft.EntityFrameworkCore.Query[10107] - (QueryContext queryContext) => IAsyncEnumerable _InterceptExceptions( - source: IAsyncEnumerable _TrackEntities( - results: IAsyncEnumerable _ShapedQuery( - queryContext: queryContext, - shaperCommandContext: SelectExpression: - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0, - shaper: UnbufferedEntityShaper), - queryContext: queryContext, - entityTrackingInfos: { itemType: TodoItem }, - entityAccessors: List> - { - Func, - } - ), - contextType: JsonApiDotNetCoreExample.Data.AppDbContext, - logger: DiagnosticsLogger, - queryContext: queryContext) -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000] - Opening connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001] - Opened connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Command[20100] - Executing DbCommand [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -info: Microsoft.EntityFrameworkCore.Database.Command[20101] - Executed DbCommand (1ms) [Parameters=[@__p_1='?', @__p_0='?'], CommandType='Text', CommandTimeout='30'] - SELECT "t"."Id", "t"."AchievedDate", "t"."AssigneeId", "t"."CollectionId", "t"."CreatedDate", "t"."Description", "t"."GuidProperty", "t"."Ordinal", "t"."OwnerId" - FROM "TodoItems" AS "t" - LIMIT @__p_1 OFFSET @__p_0 -dbug: Microsoft.EntityFrameworkCore.Database.Command[20300] - A data reader was disposed. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002] - Closing connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003] - Closed connection to database 'JsonApiDotNetCoreExample' on server 'tcp://localhost:5432'. -dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action method JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample), returned result Microsoft.AspNetCore.Mvc.OkObjectResult. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[4] - No information found on request to perform content negotiation. -dbug: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[2] - Selected output formatter 'JsonApiDotNetCore.Formatters.JsonApiOutputFormatter' and content type '' to write the response. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. -info: JsonApiDotNetCore.Formatters.JsonApiWriter[0] - Formatting response as JSONAPI -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 38.909ms -info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] - Executed action JsonApiDotNetCoreExample.Controllers.TodoItemsController.GetAsync (JsonApiDotNetCoreExample) in 38.909ms -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 63.599ms 200 application/vnd.api+json -info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] - Request finished in 63.599ms 200 application/vnd.api+json -dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[5] - Hosting shutdown -[xUnit.net 00:00:22.5994190] Finished: JsonApiDotNetCoreExampleTests - -Total tests: 111. Passed: 110. Failed: 1. Skipped: 0. -Test execution time: 23.7537 Seconds diff --git a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj index c0500c3bbd..6e2fe18abd 100644 --- a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj +++ b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj @@ -20,7 +20,7 @@ - + diff --git a/test/OperationsExampleTests/OperationsExampleTests.csproj b/test/OperationsExampleTests/OperationsExampleTests.csproj index c2c70d6e54..f84b550354 100644 --- a/test/OperationsExampleTests/OperationsExampleTests.csproj +++ b/test/OperationsExampleTests/OperationsExampleTests.csproj @@ -6,7 +6,7 @@ - + diff --git a/test/ResourceEntitySeparationExampleTests/ResourceEntitySeparationExampleTests.csproj b/test/ResourceEntitySeparationExampleTests/ResourceEntitySeparationExampleTests.csproj index e7dbeba06f..cfa496283e 100644 --- a/test/ResourceEntitySeparationExampleTests/ResourceEntitySeparationExampleTests.csproj +++ b/test/ResourceEntitySeparationExampleTests/ResourceEntitySeparationExampleTests.csproj @@ -7,8 +7,6 @@ - - @@ -19,4 +17,13 @@ + + + + + runtime; build; native; contentfiles; analyzers + all + + + diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index d95bcd46fd..1e344d064b 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -8,7 +8,7 @@ - + From d22f417a016a7576d38f875c0cade056e1e1eca9 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 27 May 2019 15:59:24 +0200 Subject: [PATCH 135/168] fix: repo context issue --- src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 48c06cfa01..fe85971302 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -463,7 +463,7 @@ public virtual IQueryable Include(IQueryable entities, string // variables mutated in recursive loop // TODO: make recursive method string internalRelationshipPath = null; - var entity = _jsonApiContext.ResourceGraph.GetContextEntity(typeof(TEntity)); + var entity = _jsonApiContext.RequestEntity; for (var i = 0; i < relationshipChain.Length; i++) { var requestedRelationship = relationshipChain[i]; From f66f8941e4985cb9378474808d1003b4ea454dc5 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 27 May 2019 16:56:17 +0200 Subject: [PATCH 136/168] comment: update --- src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index fe85971302..6fd0945cdc 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -495,7 +495,7 @@ public virtual async Task> PageAsync(IQueryable en { if (pageNumber >= 0) { - // the IQueryable returned from ApplyResourceDefinitionLogic is sometimes consumed here. + // the IQueryable returned from the hook executor is sometimes consumed here. // In this case, it does not support .ToListAsync(), so we use the method below. return await this.ToListAsync(entities.PageForward(pageSize, pageNumber)); } From 1ecab3828a6f2dbed2a4519793082b063b0dd06d Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 27 May 2019 17:19:00 +0200 Subject: [PATCH 137/168] refactor: rename pipeline enum members --- .../Resources/ArticleResource.cs | 2 +- .../Resources/PassportResource.cs | 2 +- .../Hooks/Execution/ResourcePipelineEnum.cs | 10 ++--- .../Hooks/IResourceHookContainer.cs | 6 +-- .../Hooks/ResourceHookExecutor.cs | 6 +-- .../Services/EntityResourceService.cs | 20 +++++----- .../Create/AfterCreateTests.cs | 16 ++++---- .../Create/BeforeCreateTests.cs | 18 ++++----- .../Create/BeforeCreate_WithDbValues_Tests.cs | 32 ++++++++-------- .../IdentifiableManyToMany_OnReturnTests.cs | 38 +++++++++---------- .../ManyToMany_OnReturnTests.cs | 16 ++++---- .../Read/BeforeReadTests.cs | 38 +++++++++---------- .../IdentifiableManyToMany_AfterReadTests.cs | 32 ++++++++-------- .../Read/ManyToMany_AfterReadTests.cs | 16 ++++---- .../ResourceHookExecutor/ScenarioTests.cs | 14 +++---- 15 files changed, 133 insertions(+), 133 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs index e9b4572b46..66429f175c 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs @@ -16,7 +16,7 @@ public ArticleResource(IResourceGraph graph) : base(graph) { } public override IEnumerable
OnReturn(HashSet
entities, ResourcePipeline pipeline) { - if (pipeline == ResourcePipeline.ReadSingle && entities.Single().Name == "Classified") + if (pipeline == ResourcePipeline.GetSingle && entities.Single().Name == "Classified") { throw new JsonApiException(403, "You are not allowed to see this article!", new UnauthorizedAccessException()); } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs index 6ebad453be..9955997d8b 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs @@ -16,7 +16,7 @@ public PassportResource(IResourceGraph graph) : base(graph) public override void BeforeRead(ResourcePipeline pipeline, bool nestedHook = false, string stringId = null) { - if (pipeline == ResourcePipeline.ReadSingle && nestedHook) + if (pipeline == ResourcePipeline.GetSingle && nestedHook) { throw new JsonApiException(403, "Not allowed to include passports on individual people", new UnauthorizedAccessException()); } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/ResourcePipelineEnum.cs b/src/JsonApiDotNetCore/Hooks/Execution/ResourcePipelineEnum.cs index ad4482792a..177423ed4f 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/ResourcePipelineEnum.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/ResourcePipelineEnum.cs @@ -8,14 +8,14 @@ public enum ResourcePipeline { None, - Read, - ReadSingle, - ReadRelationship, - Create, + Get, + GetSingle, + GetRelationship, + Post, Patch, PatchRelationship, Delete, - BulkCreate, + BulkPost, BulkPatch, BulkDelete } diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs index 00bc5a22ec..3cefb039e3 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs @@ -79,7 +79,7 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// Implement this hook to run custom logic in the /// layer just before creation of entities of type . /// - /// For the pipeline, + /// For the pipeline, /// will typically contain one entry. For , /// can contain multiple entities. /// @@ -103,7 +103,7 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// /// 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 + /// 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 @@ -161,7 +161,7 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// layer just before updating relationships to entities of type . /// /// This hook is fired when a relationship is created to entities of type - /// from a dependent pipeline ( + /// from a dependent pipeline ( /// or ). For example, If an Article was created /// and its author relationship was set to an existing Person, this hook will be fired /// for that particular Person. diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index 0b7739aeac..9e6b3a877c 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -94,7 +94,7 @@ public virtual IEnumerable BeforeDelete(IEnumerable e /// public virtual IEnumerable OnReturn(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable { - if (GetHook(ResourceHook.OnReturn, entities, out var container, out var node) && pipeline != ResourcePipeline.ReadRelationship) + if (GetHook(ResourceHook.OnReturn, entities, out var container, out var node) && pipeline != ResourcePipeline.GetRelationship) { IEnumerable updated = container.OnReturn((HashSet)node.UniqueEntities, pipeline); ValidateHookResponse(updated); @@ -257,7 +257,7 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, EntityChildLayer lay // fire the BeforeImplicitUpdateRelationship hook for o1 var implicitPrincipalTargets = node.RelationshipsFromPreviousLayer.GetPrincipalEntities(); - if (pipeline != ResourcePipeline.Create && implicitPrincipalTargets.Any()) + if (pipeline != ResourcePipeline.Post && implicitPrincipalTargets.Any()) { FireForAffectedImplicits(entityType, implicitPrincipalTargets, pipeline, uniqueEntities); } @@ -294,7 +294,7 @@ void FireForAffectedImplicits(Type entityType, DictionaryThe pipeine from which the hook was fired void ValidateHookResponse(IEnumerable returnedList, ResourcePipeline pipeline = 0) { - if (pipeline == ResourcePipeline.ReadSingle && returnedList.Count() > 1) + if (pipeline == ResourcePipeline.GetSingle && returnedList.Count() > 1) { throw new ApplicationException("The returned collection from this hook may contain at most one item in the case of the" + pipeline.ToString("G") + "pipeline"); diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 347223c8ce..06024d8b7c 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -97,7 +97,7 @@ public virtual async Task CreateAsync(TResource resource) { var entity = MapIn(resource); - entity = IsNull(_hookExecutor) ? entity : _hookExecutor.BeforeCreate(AsList(entity), ResourcePipeline.Create).SingleOrDefault(); + entity = IsNull(_hookExecutor) ? entity : _hookExecutor.BeforeCreate(AsList(entity), ResourcePipeline.Post).SingleOrDefault(); entity = await _entities.CreateAsync(entity); // this ensures relationships get reloaded from the database if they have @@ -113,8 +113,8 @@ public virtual async Task CreateAsync(TResource resource) } if (!IsNull(_hookExecutor, entity)) { - _hookExecutor.AfterCreate(AsList(entity), ResourcePipeline.Create); - entity = _hookExecutor.OnReturn(AsList(entity), ResourcePipeline.Read).SingleOrDefault(); + _hookExecutor.AfterCreate(AsList(entity), ResourcePipeline.Post); + entity = _hookExecutor.OnReturn(AsList(entity), ResourcePipeline.Get).SingleOrDefault(); } return MapOut(entity); } @@ -129,7 +129,7 @@ public virtual async Task DeleteAsync(TId id) public virtual async Task> GetAsync() { - _hookExecutor?.BeforeRead(ResourcePipeline.Read); + _hookExecutor?.BeforeRead(ResourcePipeline.Get); var entities = _entities.Get(); entities = ApplySortAndFilterQuery(entities); @@ -145,8 +145,8 @@ public virtual async Task> GetAsync() if (!IsNull(_hookExecutor, entities)) { var result = entities.ToList(); - _hookExecutor.AfterRead(result, ResourcePipeline.Read); - entities = _hookExecutor.OnReturn(result, ResourcePipeline.Read).AsQueryable(); + _hookExecutor.AfterRead(result, ResourcePipeline.Get); + entities = _hookExecutor.OnReturn(result, ResourcePipeline.Get).AsQueryable(); } if (_jsonApiContext.Options.IncludeTotalRecordCount) @@ -159,7 +159,7 @@ public virtual async Task> GetAsync() public virtual async Task GetAsync(TId id) { - var pipeline = ResourcePipeline.ReadSingle; + var pipeline = ResourcePipeline.GetSingle; _hookExecutor?.BeforeRead(pipeline, id.ToString()); TEntity entity; if (ShouldIncludeRelationships()) @@ -186,12 +186,12 @@ public virtual async Task GetAsync(TId id) public virtual async Task GetRelationshipAsync(TId id, string relationshipName) { - _hookExecutor?.BeforeRead(ResourcePipeline.ReadRelationship, id.ToString()); + _hookExecutor?.BeforeRead(ResourcePipeline.GetRelationship, id.ToString()); var entity = await _entities.GetAndIncludeAsync(id, relationshipName); if (!IsNull(_hookExecutor, entity)) { - _hookExecutor.AfterRead(AsList(entity), ResourcePipeline.ReadRelationship); - entity = _hookExecutor.OnReturn(AsList(entity), ResourcePipeline.ReadRelationship).SingleOrDefault(); + _hookExecutor.AfterRead(AsList(entity), ResourcePipeline.GetRelationship); + entity = _hookExecutor.OnReturn(AsList(entity), ResourcePipeline.GetRelationship).SingleOrDefault(); } // TODO: it would be better if we could distinguish whether or not the relationship was not found, diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/AfterCreateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/AfterCreateTests.cs index 97fa99f890..ca7b78f86d 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/AfterCreateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/AfterCreateTests.cs @@ -21,11 +21,11 @@ public void AfterCreate() var todoList = CreateTodoWithOwner(); // act - hookExecutor.AfterCreate(todoList, ResourcePipeline.Create); + hookExecutor.AfterCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.AfterCreate(It.IsAny>(), ResourcePipeline.Create), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.AfterCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -40,10 +40,10 @@ public void AfterCreate_Without_Parent_Hook_Implemented() var todoList = CreateTodoWithOwner(); // act - hookExecutor.AfterCreate(todoList, ResourcePipeline.Create); + hookExecutor.AfterCreate(todoList, ResourcePipeline.Post); // assert - ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Create), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -58,10 +58,10 @@ public void AfterCreate_Without_Child_Hook_Implemented() var todoList = CreateTodoWithOwner(); // act - hookExecutor.AfterCreate(todoList, ResourcePipeline.Create); + hookExecutor.AfterCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.AfterCreate(It.IsAny>(), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.AfterCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -76,7 +76,7 @@ public void AfterCreate_Without_Any_Hook_Implemented() var todoList = CreateTodoWithOwner(); // act - hookExecutor.AfterCreate(todoList, ResourcePipeline.Create); + hookExecutor.AfterCreate(todoList, ResourcePipeline.Post); // assert VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs index b32f286095..e196c04f76 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs @@ -22,10 +22,10 @@ public void BeforeCreate() var todoList = CreateTodoWithOwner(); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Create), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -41,10 +41,10 @@ public void BeforeCreate_Without_Parent_Hook_Implemented() var todoList = CreateTodoWithOwner(); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Create), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } [Fact] @@ -59,9 +59,9 @@ public void BeforeCreate_Without_Child_Hook_Implemented() var todoList = CreateTodoWithOwner(); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } [Fact] @@ -76,7 +76,7 @@ public void BeforeCreate_Without_Any_Hook_Implemented() var todoList = CreateTodoWithOwner(); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs index b5112a3401..978434823e 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs @@ -51,18 +51,18 @@ public void BeforeCreate() var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery, repoDbContextOptions: options); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.IsAny>(), - ResourcePipeline.Create), + ResourcePipeline.Post), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( It.Is>(rh => TodoCheck(rh, description + description)), - ResourcePipeline.Create), + ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -77,13 +77,13 @@ public void BeforeCreate_Without_Parent_Hook_Implemented() var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery, repoDbContextOptions: options); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.IsAny>(), - ResourcePipeline.Create), + ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -98,13 +98,13 @@ public void BeforeCreate_Without_Child_Hook_Implemented() var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery, repoDbContextOptions: options); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( It.Is>(rh => TodoCheck(rh, description + description)), - ResourcePipeline.Create), + ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -119,14 +119,14 @@ public void BeforeCreate_NoImplicit() var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery, repoDbContextOptions: options); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.IsAny>(), - ResourcePipeline.Create), + ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -141,13 +141,13 @@ public void BeforeCreate_NoImplicit_Without_Parent_Hook_Implemented() var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery, repoDbContextOptions: options); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.IsAny>(), - ResourcePipeline.Create), + ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -162,10 +162,10 @@ public void BeforeCreate_NoImplicit_Without_Child_Hook_Implemented() var ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery, repoDbContextOptions: options); // act - hookExecutor.BeforeCreate(todoList, ResourcePipeline.Create); + hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/IdentifiableManyToMany_OnReturnTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/IdentifiableManyToMany_OnReturnTests.cs index ad9616607a..fd29857c37 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/IdentifiableManyToMany_OnReturnTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/IdentifiableManyToMany_OnReturnTests.cs @@ -23,12 +23,12 @@ public void OnReturn() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Read), Times.Once()); - joinResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Read), Times.Once()); - tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read), Times.Once()); + articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Get), Times.Once()); + joinResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Get), Times.Once()); + tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -44,11 +44,11 @@ public void OnReturn_GetRelationship() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.ReadRelationship); + hookExecutor.OnReturn(articles, ResourcePipeline.GetRelationship); // assert - joinResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.ReadRelationship), Times.Once()); - tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.ReadRelationship), Times.Once()); + joinResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.GetRelationship), Times.Once()); + tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.GetRelationship), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -64,11 +64,11 @@ public void OnReturn_Without_Parent_Hook_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // assert - joinResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Read), Times.Once()); - tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read), Times.Once()); + joinResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Get), Times.Once()); + tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -85,11 +85,11 @@ public void OnReturn_Without_Children_Hooks_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Read), Times.Once()); - tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read), Times.Once()); + articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Get), Times.Once()); + tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -105,11 +105,11 @@ public void OnReturn_Without_Grand_Children_Hooks_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Read), Times.Once()); - joinResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Read), Times.Once()); + articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Get), Times.Once()); + joinResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Get), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -125,10 +125,10 @@ public void OnReturn_Without_Any_Descendant_Hooks_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Read), Times.Once()); + articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Get), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -144,7 +144,7 @@ public void OnReturn_Without_Any_Hook_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // asert VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/ManyToMany_OnReturnTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/ManyToMany_OnReturnTests.cs index b74dc47069..ad9577c3b5 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/ManyToMany_OnReturnTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/ManyToMany_OnReturnTests.cs @@ -52,11 +52,11 @@ public void OnReturn() (var articles, var joins, var tags) = CreateDummyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Read), Times.Once()); - tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read), Times.Once()); + articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Get), Times.Once()); + tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get), Times.Once()); VerifyNoOtherCalls(articleResourceMock, tagResourceMock); } @@ -71,10 +71,10 @@ public void OnReturn_Without_Parent_Hook_Implemented() (var articles, var joins, var tags) = CreateDummyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // asser - tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read), Times.Once()); + tagResourceMock.Verify(rd => rd.OnReturn(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get), Times.Once()); VerifyNoOtherCalls(articleResourceMock, tagResourceMock); } @@ -89,10 +89,10 @@ public void OnReturn_Without_Children_Hooks_Implemented() (var articles, var joins, var tags) = CreateDummyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Read), Times.Once()); + articleResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Get), Times.Once()); VerifyNoOtherCalls(articleResourceMock, tagResourceMock); } @@ -108,7 +108,7 @@ public void OnReturn_Without_Any_Hook_Implemented() (var articles, var joins, var tags) = CreateDummyData(); // act - hookExecutor.OnReturn(articles, ResourcePipeline.Read); + hookExecutor.OnReturn(articles, ResourcePipeline.Get); // assert VerifyNoOtherCalls(articleResourceMock, tagResourceMock); diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/BeforeReadTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/BeforeReadTests.cs index ef59311657..e0eceb2fd5 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/BeforeReadTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/BeforeReadTests.cs @@ -21,9 +21,9 @@ public void BeforeRead() contextMock.Setup(c => c.IncludedRelationships).Returns(new List()); // act - hookExecutor.BeforeRead(ResourcePipeline.Read); + hookExecutor.BeforeRead(ResourcePipeline.Get); // assert - todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, false, null), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, false, null), Times.Once()); VerifyNoOtherCalls(todoResourceMock); } @@ -43,10 +43,10 @@ public void BeforeReadWithInclusion() contextMock.Setup(c => c.IncludedRelationships).Returns(new List() { "owner", "assignee", "stake-holders" }); // act - hookExecutor.BeforeRead(ResourcePipeline.Read); + hookExecutor.BeforeRead(ResourcePipeline.Get); // assert - todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, false, null), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, true, null), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, false, null), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, true, null), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -66,11 +66,11 @@ public void BeforeReadWithNestedInclusion() contextMock.Setup(c => c.IncludedRelationships).Returns(new List() { "owner.passport", "assignee", "stake-holders" }); // act - hookExecutor.BeforeRead(ResourcePipeline.Read); + hookExecutor.BeforeRead(ResourcePipeline.Get); // assert - todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, false, null), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, true, null), Times.Once()); - passportResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, true, null), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, false, null), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, true, null), Times.Once()); + passportResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, true, null), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock, passportResourceMock); } @@ -91,10 +91,10 @@ public void BeforeReadWithNestedInclusion_No_Parent_Hook_Implemented() contextMock.Setup(c => c.IncludedRelationships).Returns(new List() { "owner.passport", "assignee", "stake-holders" }); // act - hookExecutor.BeforeRead(ResourcePipeline.Read); + hookExecutor.BeforeRead(ResourcePipeline.Get); // assert - ownerResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, true, null), Times.Once()); - passportResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, true, null), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, true, null), Times.Once()); + passportResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, true, null), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock, passportResourceMock); } @@ -114,10 +114,10 @@ public void BeforeReadWithNestedInclusion_No_Child_Hook_Implemented() contextMock.Setup(c => c.IncludedRelationships).Returns(new List() { "owner.passport", "assignee", "stake-holders" }); // act - hookExecutor.BeforeRead(ResourcePipeline.Read); + hookExecutor.BeforeRead(ResourcePipeline.Get); // assert - todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, false, null), Times.Once()); - passportResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, true, null), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, false, null), Times.Once()); + passportResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, true, null), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock, passportResourceMock); } @@ -137,10 +137,10 @@ public void BeforeReadWithNestedInclusion_No_Grandchild_Hook_Implemented() contextMock.Setup(c => c.IncludedRelationships).Returns(new List() { "owner.passport", "assignee", "stake-holders" }); // act - hookExecutor.BeforeRead(ResourcePipeline.Read); + hookExecutor.BeforeRead(ResourcePipeline.Get); // assert - todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, false, null), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Read, true, null), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, false, null), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeRead(ResourcePipeline.Get, true, null), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock, passportResourceMock); } @@ -161,7 +161,7 @@ public void BeforeReadWithNestedInclusion_Without_Any_Hook_Implemented() contextMock.Setup(c => c.IncludedRelationships).Returns(new List() { "owner.passport", "assignee", "stake-holders" }); // act - hookExecutor.BeforeRead(ResourcePipeline.Read); + hookExecutor.BeforeRead(ResourcePipeline.Get); // assert VerifyNoOtherCalls(todoResourceMock, ownerResourceMock, passportResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/IdentifiableManyToMany_AfterReadTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/IdentifiableManyToMany_AfterReadTests.cs index 3c472e5675..1d34524029 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/IdentifiableManyToMany_AfterReadTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/IdentifiableManyToMany_AfterReadTests.cs @@ -23,12 +23,12 @@ public void AfterRead() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Read, false), Times.Once()); - joinResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Read, true), Times.Once()); - tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read, true), Times.Once()); + articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Get, false), Times.Once()); + joinResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Get, true), Times.Once()); + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get, true), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -44,11 +44,11 @@ public void AfterRead_Without_Parent_Hook_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // assert - joinResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Read, true), Times.Once()); - tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read, true), Times.Once()); + joinResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Get, true), Times.Once()); + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get, true), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -66,11 +66,11 @@ public void AfterRead_Without_Children_Hooks_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Read, false), Times.Once()); - tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read, true), Times.Once()); + articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Get, false), Times.Once()); + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get, true), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -86,11 +86,11 @@ public void AfterRead_Without_Grand_Children_Hooks_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Read, false), Times.Once()); - joinResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Read, true), Times.Once()); + articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Get, false), Times.Once()); + joinResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(joins).Any()), ResourcePipeline.Get, true), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -106,10 +106,10 @@ public void AfterRead_Without_Any_Descendant_Hooks_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Read, false), Times.Once()); + articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Get, false), Times.Once()); VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); } @@ -125,7 +125,7 @@ public void AfterRead_Without_Any_Hook_Implemented() (var articles, var joins, var tags) = CreateIdentifiableManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // asert VerifyNoOtherCalls(articleResourceMock, joinResourceMock, tagResourceMock); diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/ManyToMany_AfterReadTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/ManyToMany_AfterReadTests.cs index 31a5896b3f..dcdf81ef94 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/ManyToMany_AfterReadTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Read/ManyToMany_AfterReadTests.cs @@ -22,11 +22,11 @@ public void AfterRead() (var articles, var joins, var tags) = CreateManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Read, false), Times.Once()); - tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read, true), Times.Once()); + articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Get, false), Times.Once()); + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get, true), Times.Once()); VerifyNoOtherCalls(articleResourceMock, tagResourceMock); } @@ -41,10 +41,10 @@ public void AfterRead_Without_Parent_Hook_Implemented() (var articles, var joins, var tags) = CreateManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // assert - tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Read, true), Times.Once()); + tagResourceMock.Verify(rd => rd.AfterRead(It.Is>((collection) => !collection.Except(tags).Any()), ResourcePipeline.Get, true), Times.Once()); VerifyNoOtherCalls(articleResourceMock, tagResourceMock); } @@ -59,10 +59,10 @@ public void AfterRead_Without_Children_Hooks_Implemented() (var articles, var joins, var tags) = CreateManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // assert - articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Read, false), Times.Once()); + articleResourceMock.Verify(rd => rd.AfterRead(It.IsAny>(), ResourcePipeline.Get, false), Times.Once()); VerifyNoOtherCalls(articleResourceMock, tagResourceMock); } @@ -77,7 +77,7 @@ public void AfterRead_Without_Any_Hook_Implemented() (var articles, var joins, var tags) = CreateManyToManyData(); // act - hookExecutor.AfterRead(articles, ResourcePipeline.Read); + hookExecutor.AfterRead(articles, ResourcePipeline.Get); // assert VerifyNoOtherCalls(articleResourceMock, tagResourceMock); diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/ScenarioTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/ScenarioTests.cs index 77bd59ef72..c79f633c83 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/ScenarioTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/ScenarioTests.cs @@ -27,11 +27,11 @@ public void Entity_Has_Multiple_Relations_To_Same_Type() var todoList = new List() { todo }; // act - hookExecutor.OnReturn(todoList, ResourcePipeline.Create); + hookExecutor.OnReturn(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Create), Times.Once()); - ownerResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Post), Times.Once()); + ownerResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -47,10 +47,10 @@ public void Entity_Has_Cyclic_Relations() var todoList = new List() { todo }; // act - hookExecutor.OnReturn(todoList, ResourcePipeline.Create); + hookExecutor.OnReturn(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Create), Times.Once()); + todoResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock); } @@ -71,10 +71,10 @@ public void Entity_Has_Nested_Cyclic_Relations() var todoList = new List() { rootTodo }; // act - hookExecutor.OnReturn(todoList, ResourcePipeline.Create); + hookExecutor.OnReturn(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Create), Times.Exactly(4)); + todoResourceMock.Verify(rd => rd.OnReturn(It.IsAny>(), ResourcePipeline.Post), Times.Exactly(4)); VerifyNoOtherCalls(todoResourceMock); } } From b5a27c4921092015a737b5ab9c9270e1149a1ac1 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 28 May 2019 09:16:07 +0200 Subject: [PATCH 138/168] refactor: renamed some internals, and added id to mapped out entity in service layer --- src/Examples/JsonApiDotNetCoreExample/Startup.cs | 2 +- src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs | 2 +- .../Extensions/IApplicationBuilderExtensions.cs | 7 +++++-- src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs | 2 +- ...udeDatabaseValues.cs => LoadDatabaseValuesAttribute.cs} | 4 ++-- .../Hooks/Execution/HookExecutorHelper.cs | 2 +- src/JsonApiDotNetCore/Services/EntityResourceService.cs | 5 +++-- test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs | 2 +- 8 files changed, 15 insertions(+), 11 deletions(-) rename src/JsonApiDotNetCore/Hooks/Discovery/{IncludeDatabaseValues.cs => LoadDatabaseValuesAttribute.cs} (57%) diff --git a/src/Examples/JsonApiDotNetCoreExample/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startup.cs index 0d5b9842a0..92a4a33112 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startup.cs @@ -40,7 +40,7 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services) options.DefaultPageSize = 5; options.IncludeTotalRecordCount = true; options.EnableResourceHooks = true; - options.DatabaseValuesInDiffs = true; + options.LoadDatabaseValues = true; }, mvcBuilder, discovery => discovery.AddCurrentAssembly()); diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index d7f3ff28de..59cbada98d 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -50,7 +50,7 @@ public class JsonApiOptions /// /// Defaults to . /// - public bool DatabaseValuesInDiffs { get; set; } = true; + public bool LoadDatabaseValues { get; set; } = true; /// /// The base URL Namespace diff --git a/src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs index 0e653155db..e66b51a7a0 100644 --- a/src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs @@ -22,8 +22,11 @@ public static IApplicationBuilder UseJsonApi(this IApplicationBuilder app, bool if (useMvc) app.UseMvc(); - var inverseRelationshipResolver = app.ApplicationServices.GetService(); - inverseRelationshipResolver?.Resolve(); + using (var scope = app.ApplicationServices.CreateScope()) + { + var inverseRelationshipResolver = scope.ServiceProvider.GetService(); + inverseRelationshipResolver?.Resolve(); + } return app; } diff --git a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs index b08a440d84..83453e7b3b 100644 --- a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs +++ b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs @@ -56,7 +56,7 @@ void DiscoverImplementedHooksForModel() diffEnabledHooks.Add(hook); continue; } - var attr = method.GetCustomAttributes(true).OfType().SingleOrDefault(); + var attr = method.GetCustomAttributes(true).OfType().SingleOrDefault(); if (attr != null) { var targetList = attr.value ? diffEnabledHooks : diffDisabledHooks; diff --git a/src/JsonApiDotNetCore/Hooks/Discovery/IncludeDatabaseValues.cs b/src/JsonApiDotNetCore/Hooks/Discovery/LoadDatabaseValuesAttribute.cs similarity index 57% rename from src/JsonApiDotNetCore/Hooks/Discovery/IncludeDatabaseValues.cs rename to src/JsonApiDotNetCore/Hooks/Discovery/LoadDatabaseValuesAttribute.cs index 717cf4f51f..6a47e9d2a0 100644 --- a/src/JsonApiDotNetCore/Hooks/Discovery/IncludeDatabaseValues.cs +++ b/src/JsonApiDotNetCore/Hooks/Discovery/LoadDatabaseValuesAttribute.cs @@ -1,10 +1,10 @@ using System; namespace JsonApiDotNetCore.Hooks { - public class IncludeDatabaseValues : Attribute + public class LoadDatabaseValues : Attribute { public readonly bool value; - public IncludeDatabaseValues(bool mode = true) + public LoadDatabaseValues(bool mode = true) { value = mode; } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index 75509d5a05..34664d138f 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -118,7 +118,7 @@ bool ShouldLoadDbValues(DependentType entityType, ResourceHook hook) } else { - return _context.Options.DatabaseValuesInDiffs; + return _context.Options.LoadDatabaseValues; } } diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 06024d8b7c..55832ca78c 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -215,13 +215,14 @@ public virtual async Task GetRelationshipAsync(TId id, string relationsh public virtual async Task UpdateAsync(TId id, TResource resource) { var entity = MapIn(resource); - + // why is entity passed along to UpdateAsync if it is never used internally?? + // I think we should just set the id on the request-parsed entity and pass that along to the repo. + entity.Id = id; entity = IsNull(_hookExecutor) ? entity : _hookExecutor.BeforeUpdate(AsList(entity), ResourcePipeline.Patch).SingleOrDefault(); entity = await _entities.UpdateAsync(id, entity); if (!IsNull(_hookExecutor, entity)) { - // TODO: should not fire after read for L=0 _hookExecutor.AfterUpdate(AsList(entity), ResourcePipeline.Patch); entity = _hookExecutor.OnReturn(AsList(entity), ResourcePipeline.Patch).SingleOrDefault(); } diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 74961947da..6fc6baa639 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -302,7 +302,7 @@ void MockHooks(Mock> resourceDefinition) var processorFactory = new Mock(); var context = new Mock(); context.Setup(c => c.GenericProcessorFactory).Returns(processorFactory.Object); - context.Setup(c => c.Options).Returns(new JsonApiOptions { DatabaseValuesInDiffs = false }); + context.Setup(c => c.Options).Returns(new JsonApiOptions { LoadDatabaseValues = false }); context.Setup(c => c.ResourceGraph).Returns(ResourceGraph.Instance); return (context, processorFactory); From d089461b8f0754c4ec0506860ab74129fb87c856 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 28 May 2019 10:39:21 +0200 Subject: [PATCH 139/168] test: inheritance --- .../Models/IIsLockable.cs | 7 ++++ .../JsonApiDotNetCoreExample/Models/Person.cs | 2 +- .../Models/TodoItem.cs | 2 +- .../Resources/LockableResourceBase.cs | 25 +++++++++++++ .../Resources/PersonResource.cs | 21 ++--------- .../Resources/TodoResource.cs | 21 ++--------- .../Resources/UserResource.cs | 5 +-- .../UnitTests/ResourceHooks/DiscoveryTests.cs | 37 +++++++++++++++---- 8 files changed, 71 insertions(+), 49 deletions(-) create mode 100644 src/Examples/JsonApiDotNetCoreExample/Models/IIsLockable.cs create mode 100644 src/Examples/JsonApiDotNetCoreExample/Resources/LockableResourceBase.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/IIsLockable.cs b/src/Examples/JsonApiDotNetCoreExample/Models/IIsLockable.cs new file mode 100644 index 0000000000..fe7d07ad34 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/IIsLockable.cs @@ -0,0 +1,7 @@ +namespace JsonApiDotNetCoreExample.Models +{ + public interface IIsLockable + { + bool IsLocked { get; set; } + } +} \ No newline at end of file diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs index 40ff35bbc1..47dc221870 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs @@ -11,7 +11,7 @@ public class PersonRole : Identifiable public Person Person { get; set; } } - public class Person : Identifiable, IHasMeta + public class Person : Identifiable, IHasMeta, IIsLockable { public bool IsLocked { get; set; } diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs index 325c80a440..3633a16ca7 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCoreExample.Models { - public class TodoItem : Identifiable + public class TodoItem : Identifiable, IIsLockable { public TodoItem() { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResourceBase.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResourceBase.cs new file mode 100644 index 0000000000..7e2ce4f658 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResourceBase.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCoreExample.Models; + +namespace JsonApiDotNetCoreExample.Resources +{ + public abstract class LockableResourceBase : ResourceDefinition where T : class, IIsLockable, IIdentifiable + { + protected LockableResourceBase(IResourceGraph graph) : base(graph) { } + + protected void DisallowLocked(IEnumerable entities) + { + foreach (var e in entities ?? Enumerable.Empty()) + { + if (e.IsLocked) + { + throw new JsonApiException(403, "Not allowed to update fields or relations of locked todo item", new UnauthorizedAccessException()); + } + } + } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs index 5731ab523c..191a70a89a 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs @@ -1,18 +1,14 @@ -using System; using System.Collections.Generic; using System.Linq; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Hooks; using JsonApiDotNetCoreExample.Models; namespace JsonApiDotNetCoreExample.Resources { - public class PersonResource : ResourceDefinition + public class PersonResource : LockableResourceBase { - public PersonResource(IResourceGraph graph) : base(graph) - { - } + public PersonResource(IResourceGraph graph) : base(graph) { } public override IEnumerable BeforeUpdateRelationship(HashSet ids, IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { @@ -22,18 +18,7 @@ public override IEnumerable BeforeUpdateRelationship(HashSet ids public override void BeforeImplicitUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { - resourcesByRelationship.GetByRelationship().ToList().ForEach(kvp => DoesNotTouchLockedPeople(kvp.Value)); - } - - private void DoesNotTouchLockedPeople(IEnumerable entities) - { - foreach (var person in entities ?? Enumerable.Empty()) - { - if (person.IsLocked) - { - throw new JsonApiException(403, "Not allowed to update fields or relations of locked persons", new UnauthorizedAccessException()); - } - } + resourcesByRelationship.GetByRelationship().ToList().ForEach(kvp => DisallowLocked(kvp.Value)); } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs index 411a572186..1de3a02a6a 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs @@ -2,17 +2,14 @@ using System.Collections.Generic; using System.Linq; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Hooks; using JsonApiDotNetCoreExample.Models; namespace JsonApiDotNetCoreExample.Resources { - public class TodoResource : ResourceDefinition + public class TodoResource : LockableResourceBase { - public TodoResource(IResourceGraph graph) : base(graph) - { - } + public TodoResource(IResourceGraph graph) : base(graph) { } public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { @@ -25,19 +22,7 @@ public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = fal public override void BeforeImplicitUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { List todos = resourcesByRelationship.GetByRelationship().SelectMany(kvp => kvp.Value).ToList(); - DoesNotTouchLocked(todos); - } - - - private void DoesNotTouchLocked(IEnumerable entities) - { - foreach (var person in entities ?? Enumerable.Empty()) - { - if (person.IsLocked) - { - throw new JsonApiException(403, "Not allowed to update fields or relations of locked todo item", new UnauthorizedAccessException()); - } - } + DisallowLocked(todos); } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index 7b7bf77920..ec54b6144e 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -9,9 +9,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class UserResource : ResourceDefinition { - public UserResource(IResourceGraph graph) : base(graph) - { - } + public UserResource(IResourceGraph graph) : base(graph) { } protected override List OutputAttrs() => Remove(user => user.Password); @@ -24,7 +22,6 @@ public override QueryFilters GetQueryFilters() }; } - private IQueryable FirstCharacterFilter(IQueryable users, FilterQuery filterQuery) { switch(filterQuery.Operation) diff --git a/test/UnitTests/ResourceHooks/DiscoveryTests.cs b/test/UnitTests/ResourceHooks/DiscoveryTests.cs index d24d47be1e..3b30c7d8ad 100644 --- a/test/UnitTests/ResourceHooks/DiscoveryTests.cs +++ b/test/UnitTests/ResourceHooks/DiscoveryTests.cs @@ -3,11 +3,20 @@ using System.Collections.Generic; using Xunit; using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal; namespace UnitTests.ResourceHooks { public class DiscoveryTests { + public class Dummy : Identifiable { } + public class DummyResourceDefinition : ResourceDefinition + { + public DummyResourceDefinition() : base(new ResourceGraphBuilder().AddResource().Build()) { } + + public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } + } [Fact] public void Hook_Discovery() @@ -19,15 +28,29 @@ public void Hook_Discovery() Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); } - public class Dummy : Identifiable { } - public class DummyResourceDefinition : ResourceDefinition + + + public class AnotherDummy : Identifiable { } + public abstract class ResourceDefintionBase : ResourceDefinition where T : class, IIdentifiable { - public DummyResourceDefinition() : base(new ResourceGraphBuilder().AddResource().Build()) - { - } + protected ResourceDefintionBase(IResourceGraph graph) : base(graph) { } + } - public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } - public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } + public class AnotherDummyResourceDefinition : ResourceDefintionBase + { + public AnotherDummyResourceDefinition() : base(new ResourceGraphBuilder().AddResource().Build()) { } + + public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } + } + [Fact] + public void Hook_Discovery_With_Inheritance() + { + // arrange & act + var hookConfig = new HooksDiscovery(); + // assert + Assert.Contains(ResourceHook.BeforeDelete, hookConfig.ImplementedHooks); + Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); } } } From d7e71d8f1181fadea6f6e30a18ede21f5300a9fe Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 28 May 2019 11:17:31 +0200 Subject: [PATCH 140/168] fix: dont include when IsInclude is false --- src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs index f0069b5185..6e4ccab1b3 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs @@ -185,6 +185,7 @@ HashSet ProcessEntities(IEnumerable incomingEntities) var contextEntity = _graph.GetContextEntity(type); foreach (RelationshipAttribute attr in contextEntity.Relationships) { + if (!attr.CanInclude) continue; if (!RelationshipProxies.TryGetValue(attr, out RelationshipProxy proxies)) { DependentType dependentType = GetDependentTypeFromRelationship(attr); From d84f30f7505f287bb81393a2b11528855e2689c5 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 28 May 2019 11:36:39 +0200 Subject: [PATCH 141/168] feat: AuthResourceBase with default hook implementation --- src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs | 5 +++-- test/UnitTests/ResourceHooks/DiscoveryTests.cs | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs index 83453e7b3b..5e19da4392 100644 --- a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs +++ b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs @@ -36,7 +36,8 @@ public HooksDiscovery() /// The implemented hooks for model. void DiscoverImplementedHooksForModel() { - var derivedTypes = TypeLocator.GetDerivedTypes(typeof(TEntity).Assembly, typeof(ResourceDefinition)).ToList(); + Type parameterizedResourceDefinition = typeof(ResourceDefinition); + var derivedTypes = TypeLocator.GetDerivedTypes(typeof(TEntity).Assembly, parameterizedResourceDefinition).ToList(); try { var implementedHooks = new List(); @@ -48,7 +49,7 @@ void DiscoverImplementedHooksForModel() foreach (var hook in _allHooks) { var method = targetType.GetMethod(hook.ToString("G")); - if (method.DeclaringType == targetType) + if (method.DeclaringType != parameterizedResourceDefinition) { implementedHooks.Add(hook); if (hook == ResourceHook.BeforeImplicitUpdateRelationship) diff --git a/test/UnitTests/ResourceHooks/DiscoveryTests.cs b/test/UnitTests/ResourceHooks/DiscoveryTests.cs index 3b30c7d8ad..9978d52dd4 100644 --- a/test/UnitTests/ResourceHooks/DiscoveryTests.cs +++ b/test/UnitTests/ResourceHooks/DiscoveryTests.cs @@ -34,14 +34,14 @@ public class AnotherDummy : Identifiable { } public abstract class ResourceDefintionBase : ResourceDefinition where T : class, IIdentifiable { protected ResourceDefintionBase(IResourceGraph graph) : base(graph) { } + + public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } } public class AnotherDummyResourceDefinition : ResourceDefintionBase { public AnotherDummyResourceDefinition() : base(new ResourceGraphBuilder().AddResource().Build()) { } - - public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } - public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } } [Fact] public void Hook_Discovery_With_Inheritance() From c284ed92b2584786ceb53fc8d0dd8e7d19da9180 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 29 May 2019 16:37:34 +0200 Subject: [PATCH 142/168] chore: rename member --- .../Hooks/Execution/UpdatedRelationshipHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs index f721ba93dc..58535d68e8 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs @@ -16,7 +16,7 @@ public interface IAffectedRelationships : IAffectedRelationships whe /// /// Gets a dictionary of all entities grouped by affected relationship. /// - Dictionary> GetAll { get; } + Dictionary> All { get; } /// /// Gets a dictionary of all entities that have an affected relationship to type /// @@ -31,7 +31,7 @@ public class UpdatedRelationshipHelper : IAffectedRelationships> _groups; - public Dictionary> GetAll + public Dictionary> All { get { return _groups?.ToDictionary(p => p.Key.Attribute, p => p.Value); } } From 0a3605da6c7e55c2d9727138f6782809bee70272 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 30 May 2019 13:31:50 +0200 Subject: [PATCH 143/168] documentation: hooks --- docs/usage/resources/hooks.md | 126 +++++++++++++++++++++++++++++++--- 1 file changed, 117 insertions(+), 9 deletions(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index 5c021706f4..326fba6845 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -1,3 +1,4 @@ + # Resource Hooks This section covers the usage of **Resource Hooks**, which is a feature of`ResourceDefinition`. See the [ResourceDefinition usage guide](resource-definitions.md) for a general explanation on how to set up a `ResourceDefinition`. @@ -13,14 +14,15 @@ Understanding the semantics will be helpful in identifying which hooks on `Resou 2. [**Hook execution overview**](#hook-execution-overview) A table overview of all pipelines and involved hooks 3. [**Examples: basic usage**](#examples-basic-usage) - * [**Most minimal example**](#most-minimal-example) - * [**Logging**](#logging) - * [**Transforming data with OnReturn**](#transforming-data-with-onreturn) + * [**Getting started: most minimal example**](#getting-started-most-minimal-example) + * [**Logging**](#logging) + * [**Transforming data with OnReturn**](#transforming-data-with-onreturn) + * [**Loading database values**](#loading-database-values) 5. [**Examples: advanced usage**](#examples-advanced-usage) - * [**Simple authorization: explicitly affected resources**](#simple-authorization-explicitly-affected-resources) - * [**Advanced authorization: implicitly affected resources**](#advanced-authorization-implicitly-affected-resources) - * [**Synchronizing data across microservices**](#synchronizing-data-across-microservices) - * [**Hooks for many-to-many join tables**](#hooks-for-many-to-many-join-tables) + * [**Simple authorization: explicitly affected resources**](#simple-authorization-explicitly-affected-resources) + * [**Advanced authorization: implicitly affected resources**](#advanced-authorization-implicitly-affected-resources) + * [**Synchronizing data across microservices**](#synchronizing-data-across-microservices) + * [**Hooks for many-to-many join tables**](#hooks-for-many-to-many-join-tables) # 1. Semantics: pipelines, actions and hooks @@ -168,8 +170,26 @@ This table below shows the involved hooks per pipeline. # 3. Examples: basic usage -## Most minimal example -The simplest example does not require much explanation. This hook would triggered by any default JsonApiDotNetCore API route for `Article`. +## Getting started: most minimal example +To use resource hooks, you are required to turn them on in your `startup.cs` configuration + +```c# +public void ConfigureServices(IServiceCollection services) +{ + ... + services.AddJsonApi( + options => + { + options.EnableResourceHooks = true; // default is false + options.LoadDatabaseValues = false; // default is true + } + ); + ... +} +``` +For this example, we may set `LoadDatabaseValues` to `false`. See the [Loading database values](#loading-database-values) example for more information about this option. + +The simplest case of resource hooks we can then implement should not require a lot of explanation. This hook would triggered by any default JsonApiDotNetCore API route for `Article`. ```c# public class ArticleResource : ResourceDefinition
{ @@ -294,6 +314,94 @@ public class PersonResource : ResourceDefinition ``` Note that not only anonymous people will be excluded when directly performing a `GET /people`, but also when included through relationships, like `GET /articles?include=author,reviewers`. Simultaneously, `if` condition that checks for `ResourcePipeline.Get` in the `PersonResource` ensures we still get expected responses from the API when eg. creating a person with `WantsPrivacy` set to true. +## Loading database values +When a hook is executed for a particular resource, JsonApiDotNetCore can load the corresponding database values and provide them in the hooks. This can be useful for eg. + * having a diff between a previous and new state of a resource (for example when updating a resource) + * performing authorization rules based on the property of a resource. + +For example, consider a scenario in with the following two requirements: +* We need to log all updates on resources revealing their old and new value. +* We need to check if the property `IsLocked` is set is `true`, and if so, cancel the operation. + + Consider an `Article` with title *Hello there* and API user trying to update the the title of this article to *Bye bye*. The above requirements could be implemented as follows +```c# +public class ArticleResource : ResourceDefinition
+{ + private readonly ILogger _logger; + private readonly IJsonApiContext _context; + public constructor ArticleResource(ILogger logger, IJsonApiContext context) + { + _logger = logger; + _context = context; + } + + public override IEnumerable
BeforeUpdate(EntityDiff
entityDiff, ResourcePipeline pipeline) + { + // PropertyGetter is a helper class that takes care of accessing the values on an instance of Article using reflection. + var getter = new PropertyGetter
(); + + entityDiff.RequestEntities.ForEach( requestEntity => + { + + var databaseEntity = entityDiff.DatabaseEntities.Single( e => e.Id == a.Id); + + if (databaseEntity.IsLocked) throw new JsonApiException(403, "Forbidden: this article is locked!") + + foreach (var attr in _context.AttributesToUpdate) + { + var oldValue = getter(databaseEntity, attr); + var newValue = getter(requestEntity, attr); + + _logger.LogAttributeUpdate(oldValue, newValue) + } + }); + return entityDiff.RequestEntities; + } +} +``` + +Note that database values are turned on by default. They can be turned of globally by configuring the startup as follows: +```c# +public void ConfigureServices(IServiceCollection services) +{ + ... + services.AddJsonApi( + options => + { + options.LoadDatabaseValues = false; // default is true + } + ); + ... +} +``` + +The global setting can be used together with toggling the option on and off on the level of individual hooks using the `LoadDatabaseValues` attribute: +```c# +public class ArticleResource : ResourceDefinition
+{ + [LoadDatabaseValues(true)] + public override IEnumerable
BeforeUpdate(EntityDiff
entityDiff, ResourcePipeline pipeline) + { + .... + } + + [LoadDatabaseValues(false)] + public override IEnumerable BeforeUpdateRelationships(HashSet ids, IAffectedRelationships
resourcesByRelationship, ResourcePipeline pipeline) + { + // the entities stored in the IAffectedRelationships
instance + // are plain resource identifier objects when LoadDatabaseValues is turned off, + // or objects loaded from the database when LoadDatabaseValues is turned on. + .... + } +} +``` + +Note that there are some hooks that the `LoadDatabaseValues` option and attribute does not affect. The only hooks that are affected are: +* `BeforeUpdate` +* `BeforeUpdateRelationship` + + + # 3. Examples: advanced usage ## Simple authorization: explicitly affected resources From b4777b444d4fd9da5d7fdf0a259fb8ce911a4a79 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Fri, 31 May 2019 09:14:31 +0200 Subject: [PATCH 144/168] docs: update hooks --- docs/usage/resources/hooks.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index 326fba6845..d7194fbfeb 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -14,15 +14,15 @@ Understanding the semantics will be helpful in identifying which hooks on `Resou 2. [**Hook execution overview**](#hook-execution-overview) A table overview of all pipelines and involved hooks 3. [**Examples: basic usage**](#examples-basic-usage) - * [**Getting started: most minimal example**](#getting-started-most-minimal-example) - * [**Logging**](#logging) - * [**Transforming data with OnReturn**](#transforming-data-with-onreturn) - * [**Loading database values**](#loading-database-values) + * [**Getting started: most minimal example**](#getting-started-most-minimal-example) + * [**Logging**](#logging) + * [**Transforming data with OnReturn**](#transforming-data-with-onreturn) + * [**Loading database values**](#loading-database-values) 5. [**Examples: advanced usage**](#examples-advanced-usage) - * [**Simple authorization: explicitly affected resources**](#simple-authorization-explicitly-affected-resources) - * [**Advanced authorization: implicitly affected resources**](#advanced-authorization-implicitly-affected-resources) - * [**Synchronizing data across microservices**](#synchronizing-data-across-microservices) - * [**Hooks for many-to-many join tables**](#hooks-for-many-to-many-join-tables) + * [**Simple authorization: explicitly affected resources**](#simple-authorization-explicitly-affected-resources) + * [**Advanced authorization: implicitly affected resources**](#advanced-authorization-implicitly-affected-resources) + * [**Synchronizing data across microservices**](#synchronizing-data-across-microservices) + * [**Hooks for many-to-many join tables**](#hooks-for-many-to-many-join-tables) # 1. Semantics: pipelines, actions and hooks From a8e35250905b5eaea0bd93603444d946ba68fdd7 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Fri, 31 May 2019 09:19:01 +0200 Subject: [PATCH 145/168] docs: update hooks guide --- docs/usage/resources/hooks.md | 168 +++++++++++++++++----------------- 1 file changed, 83 insertions(+), 85 deletions(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index d7194fbfeb..1274f04136 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -1,6 +1,6 @@ # Resource Hooks -This section covers the usage of **Resource Hooks**, which is a feature of`ResourceDefinition`. See the [ResourceDefinition usage guide](resource-definitions.md) for a general explanation on how to set up a `ResourceDefinition`. +This section covers the usage of **Resource Hooks**, which is a feature of`ResourceDefinition`. See the [ResourceDefinition usage guide](resource-definitions.md) for a general explanation on how to set up a `ResourceDefinition`. For a quick start, jump right to the [Getting started: most minimal example](#getting-started-most-minimal-example) section. By implementing resource hooks on a `ResourceDefintion`, it is possible to intercept the execution of the **Resource Service Layer** (RSL) in various ways. This enables the developer to conveniently define business logic without having to override the RSL. It can be used to implement e.g. * Authorization @@ -11,19 +11,18 @@ By implementing resource hooks on a `ResourceDefintion`, it is possible to in This usage guide covers the following sections 1. [**Semantics: pipelines, actions and hooks**](#semantics-pipelines-actions-and-hooks). Understanding the semantics will be helpful in identifying which hooks on `ResourceDefinition` you need to implement for your use-case. -2. [**Hook execution overview**](#hook-execution-overview) -A table overview of all pipelines and involved hooks -3. [**Examples: basic usage**](#examples-basic-usage) +2. [**Basic usage**](#basic-usage) * [**Getting started: most minimal example**](#getting-started-most-minimal-example) * [**Logging**](#logging) * [**Transforming data with OnReturn**](#transforming-data-with-onreturn) * [**Loading database values**](#loading-database-values) -5. [**Examples: advanced usage**](#examples-advanced-usage) +3. [**Advanced usage**](#advanced-usage) * [**Simple authorization: explicitly affected resources**](#simple-authorization-explicitly-affected-resources) * [**Advanced authorization: implicitly affected resources**](#advanced-authorization-implicitly-affected-resources) * [**Synchronizing data across microservices**](#synchronizing-data-across-microservices) * [**Hooks for many-to-many join tables**](#hooks-for-many-to-many-join-tables) - +4. [**Hook execution overview**](#hook-execution-overview) + A table overview of all pipelines and involved hooks # 1. Semantics: pipelines, actions and hooks @@ -91,84 +90,7 @@ Any return content can be intercepted and transformed as desired by implementing

For an overview of all pipelines, hooks and actions, see the table below, and for more detailed information about the available hooks, see the [IResourceHookContainer](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/ab1f96d8255532461da47d290c5440b9e7e6a4a5/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs) interface. -# 2. Hook execution overview - - -This table below shows the involved hooks per pipeline. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PipelineExecution Flow
Before HooksRepository ActionsAfter HooksOnReturn
GetBeforeReadreadAfterRead
GetSingleBeforeReadAfterRead
GetRelationshipBeforeReadAfterRead
PostBeforeCreatecreate
update relationship
AfterCreate
PatchBeforeUpdate
BeforeUpdateRelationship
BeforeImplicitUpdateRelationship
update
update relationship
implicit update relationship
AfterUpdate
AfterUpdateRelationship
PatchRelationshipBeforeUpdate
BeforeUpdateRelationship
update
update relationship
implicit update relationship
AfterUpdate
AfterUpdateRelationship
DeleteBeforeDeletedelete
implicit update relationship
AfterDelete
BulkPostNot yet supported
BulkPatchNot yet supported
BulkDeleteNot yet supported
- - -# 3. Examples: basic usage +# 2. Basic usage ## Getting started: most minimal example To use resource hooks, you are required to turn them on in your `startup.cs` configuration @@ -402,7 +324,7 @@ Note that there are some hooks that the `LoadDatabaseValues` option and attribu -# 3. Examples: advanced usage +# 3. Advanced usage ## Simple authorization: explicitly affected resources Resource hooks can be used to easily implement authorization in your application. As an example, consider the case in which an API user is not allowed to see anonymous people, which is reflected by the `Anonymous` property on `Person` being set to true`true`. The API should handle this as follows: @@ -619,3 +541,79 @@ Then, for the same request `GET /articles?include=tags`, the order of execution And the included collection of tags per article will only contain tags that were added less than two weeks ago. Note that the introduced inheritance and added relationship attributes does not further affect the many-to-many relationship internally. + +# 4. Hook execution overview + + +This table below shows the involved hooks per pipeline. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PipelineExecution Flow
Before HooksRepository ActionsAfter HooksOnReturn
GetBeforeReadreadAfterRead
GetSingleBeforeReadAfterRead
GetRelationshipBeforeReadAfterRead
PostBeforeCreatecreate
update relationship
AfterCreate
PatchBeforeUpdate
BeforeUpdateRelationship
BeforeImplicitUpdateRelationship
update
update relationship
implicit update relationship
AfterUpdate
AfterUpdateRelationship
PatchRelationshipBeforeUpdate
BeforeUpdateRelationship
update
update relationship
implicit update relationship
AfterUpdate
AfterUpdateRelationship
DeleteBeforeDeletedelete
implicit update relationship
AfterDelete
BulkPostNot yet supported
BulkPatchNot yet supported
BulkDeleteNot yet supported
From 30f6e65503f3278993e1e45a65031ce24ddaa13b Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Fri, 31 May 2019 09:21:19 +0200 Subject: [PATCH 146/168] docs: fix typo hooks" --- docs/usage/resources/hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index 1274f04136..6156267602 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -9,7 +9,7 @@ By implementing resource hooks on a `ResourceDefintion`, it is possible to in * Transformation of the served data This usage guide covers the following sections -1. [**Semantics: pipelines, actions and hooks**](#semantics-pipelines-actions-and-hooks). +1. [**Semantics: pipelines, actions and hooks**](#semantics-pipelines-actions-and-hooks) Understanding the semantics will be helpful in identifying which hooks on `ResourceDefinition` you need to implement for your use-case. 2. [**Basic usage**](#basic-usage) * [**Getting started: most minimal example**](#getting-started-most-minimal-example) From e924833ea4a5bcee7c65382e2d9a67ea6314f432 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 4 Jun 2019 15:20:44 +0200 Subject: [PATCH 147/168] chore: default value option --- docs/usage/resources/hooks.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index 6156267602..68c16f5064 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -103,7 +103,7 @@ public void ConfigureServices(IServiceCollection services) options => { options.EnableResourceHooks = true; // default is false - options.LoadDatabaseValues = false; // default is true + options.LoadDatabaseValues = false; // default is false } ); ... @@ -290,7 +290,7 @@ public void ConfigureServices(IServiceCollection services) services.AddJsonApi( options => { - options.LoadDatabaseValues = false; // default is true + options.LoadDatabaseValues = false; // default is false } ); ... From 50d34b0ccb37b435892d8f10b7248fdccda50c1e Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 4 Jun 2019 15:33:20 +0200 Subject: [PATCH 148/168] fix: default option loaddbvalues --- src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs | 4 ++-- src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index 59cbada98d..906ee4fba8 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -48,9 +48,9 @@ public class JsonApiOptions /// Whether or not database values should be included by default /// for resource hooks. Ignored if EnableResourceHooks is set false. /// - /// Defaults to . + /// Defaults to . /// - public bool LoadDatabaseValues { get; set; } = true; + public bool LoadDatabaseValues { get; set; } = false; /// /// The base URL Namespace diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index 34664d138f..c119393aa0 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -106,6 +106,8 @@ public HashSet LoadDbValues(IEnumerable entities, Res bool ShouldLoadDbValues(DependentType entityType, ResourceHook hook) { + if (hook == ResourceHook.BeforeImplicitUpdateRelationship) return true; + var discovery = GetHookDiscovery(entityType); if (discovery.DatabaseDiffDisabledHooks.Contains(hook)) From 762210eaca206b523261d6cb385d662e2dc799fa Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 4 Jun 2019 18:06:47 +0200 Subject: [PATCH 149/168] tests: DRCU update tests --- .../Hooks/Execution/HookExecutorHelper.cs | 16 +++++++--------- .../Hooks/Execution/IHookExecutorHelper.cs | 2 +- .../Hooks/ResourceHookExecutor.cs | 8 ++++---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index c119393aa0..134627c0de 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -81,15 +81,15 @@ public IResourceHookContainer GetResourceHookContainer(Resourc return (IResourceHookContainer)GetResourceHookContainer(typeof(TEntity), hook); } - public IEnumerable LoadDbValues(DependentType entityType, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships) + public IEnumerable LoadDbValues(PrincipalType repositoryEntityType, Type affectedHookEntityType, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships) { - if (!ShouldLoadDbValues(entityType, hook)) return null; + if (!ShouldLoadDbValues(affectedHookEntityType, hook)) return null; var paths = relationships.Select(p => p.Attribute.RelationshipPath).ToArray(); - var idType = GetIdentifierType(entityType); + var idType = GetIdentifierType(repositoryEntityType); var parameterizedGetWhere = GetType() .GetMethod(nameof(GetWhereAndInclude), BindingFlags.NonPublic | BindingFlags.Instance) - .MakeGenericMethod(entityType, idType); + .MakeGenericMethod(repositoryEntityType, idType); var casted = ((IEnumerable)entities).Cast(); var ids = casted.Select(e => e.StringId).Cast(idType); var values = (IEnumerable)parameterizedGetWhere.Invoke(this, new object[] { ids, paths }); @@ -98,7 +98,8 @@ public IEnumerable LoadDbValues(DependentType entityType, IEnumerable entities, public HashSet LoadDbValues(IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships) where TEntity : class, IIdentifiable { - var dbValues = LoadDbValues(typeof(TEntity), entities, hook, relationships)?.Cast(); + var entityType = typeof(TEntity); + var dbValues = LoadDbValues(entityType, entityType, entities, hook, relationships)?.Cast(); if (dbValues == null) return null; return new HashSet(dbValues); } @@ -106,8 +107,6 @@ public HashSet LoadDbValues(IEnumerable entities, Res bool ShouldLoadDbValues(DependentType entityType, ResourceHook hook) { - if (hook == ResourceHook.BeforeImplicitUpdateRelationship) return true; - var discovery = GetHookDiscovery(entityType); if (discovery.DatabaseDiffDisabledHooks.Contains(hook)) @@ -122,7 +121,6 @@ bool ShouldLoadDbValues(DependentType entityType, ResourceHook hook) { return _context.Options.LoadDatabaseValues; } - } bool ShouldExecuteHook(DependentType entityType, ResourceHook hook) @@ -180,7 +178,7 @@ public Dictionary LoadImplicitlyAffected( foreach (var kvp in principalEntitiesByRelation) { if (IsHasManyThrough(kvp, out var principals, out var relationship)) continue; - var includedPrincipals = LoadDbValues(relationship.PrincipalType, principals, ResourceHook.BeforeImplicitUpdateRelationship, relationship); + var includedPrincipals = LoadDbValues(relationship.PrincipalType, relationship.DependentType, principals, ResourceHook.BeforeImplicitUpdateRelationship, relationship); foreach (IIdentifiable ip in includedPrincipals) { diff --git a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs index 2ddad4b99e..5e2f4f5e6f 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs @@ -42,7 +42,7 @@ internal interface IHookExecutorHelper /// /// For a set of entities, loads current values from the database /// - IEnumerable LoadDbValues(Type entityType, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships); + IEnumerable LoadDbValues(Type repositoryEntityType, Type affectedHookEntityType, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships); /// /// For a set of entities, loads current values from the database /// diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index 9e6b3a877c..42ffe18754 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -246,7 +246,7 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, EntityChildLayer lay { if (uniqueEntities.Cast().Any()) { - var dbValues = _executorHelper.LoadDbValues(entityType, uniqueEntities, ResourceHook.BeforeUpdateRelationship, node.RelationshipsToNextLayer); + var dbValues = _executorHelper.LoadDbValues(entityType, entityType, uniqueEntities, ResourceHook.BeforeUpdateRelationship, node.RelationshipsToNextLayer); var resourcesByRelationship = CreateRelationshipHelper(entityType, node.RelationshipsFromPreviousLayer.GetDependentEntities(), dbValues); var allowedIds = CallHook(nestedHookcontainer, ResourceHook.BeforeUpdateRelationship, new object[] { GetIds(uniqueEntities), resourcesByRelationship, pipeline }).Cast(); var updated = GetAllowedEntities(uniqueEntities, allowedIds); @@ -276,13 +276,13 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, EntityChildLayer lay /// Given a source of entities, gets the implicitly affected entities /// from the database and calls the BeforeImplicitUpdateRelationship hook. /// - void FireForAffectedImplicits(Type entityType, Dictionary implicitsTarget, ResourcePipeline pipeline, IEnumerable existingImplicitEntities = null) + void FireForAffectedImplicits(Type entityTypeToInclude, Dictionary implicitsTarget, ResourcePipeline pipeline, IEnumerable existingImplicitEntities = null) { - var container = _executorHelper.GetResourceHookContainer(entityType, ResourceHook.BeforeImplicitUpdateRelationship); + var container = _executorHelper.GetResourceHookContainer(entityTypeToInclude, ResourceHook.BeforeImplicitUpdateRelationship); if (container == null) return; var implicitAffected = _executorHelper.LoadImplicitlyAffected(implicitsTarget, existingImplicitEntities); if (!implicitAffected.Any()) return; - var resourcesByRelationship = CreateRelationshipHelper(entityType, implicitAffected); + var resourcesByRelationship = CreateRelationshipHelper(entityTypeToInclude, implicitAffected); CallHook(container, ResourceHook.BeforeImplicitUpdateRelationship, new object[] { resourcesByRelationship, pipeline, }); } From bcf2dbcfde67fe0938b6106b9225a8a754570a3a Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 11:17:04 +0200 Subject: [PATCH 150/168] chore: rm forgotten line --- src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 6fd0945cdc..9200d21135 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -127,7 +127,6 @@ public virtual IQueryable Filter(IQueryable entities, FilterQu return defaultQueryFilter(entities, filterQuery); } } - var x = _genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), typeof(TEntity)); return entities.Filter(_jsonApiContext, filterQuery); } From 61c2e9383a89d8c8eb62cad1c376e56e38caa808 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 12:25:42 +0200 Subject: [PATCH 151/168] fix: removed usage of guid-based repositories --- .../Data/DefaultEntityRepository.cs | 24 ------------------- .../Data/IEntityReadRepository.cs | 5 ---- .../Data/IEntityRepository.cs | 4 ---- .../Data/IEntityWriteRepository.cs | 5 ---- .../IServiceCollectionExtensions.cs | 6 ++++- .../Graph/ServiceDiscoveryFacade.cs | 3 --- .../Hooks/Execution/HookExecutorHelper.cs | 3 +-- .../ResourceHooks/ResourceHooksTestsSetup.cs | 6 ++--- 8 files changed, 9 insertions(+), 47 deletions(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 9200d21135..377b330cac 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -38,29 +38,6 @@ public DefaultEntityRepository( { } } - public class DefaultGuidEntityRepository - : DefaultEntityRepository, - IGuidEntityRepository - where TEntity : class, IIdentifiable - { - public DefaultGuidEntityRepository( - IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver - ) - : base(jsonApiContext, contextResolver) - { } - - public DefaultGuidEntityRepository( - ILoggerFactory loggerFactory, - IJsonApiContext jsonApiContext, - IDbContextResolver contextResolver, - ResourceDefinition resourceDefinition = null - ) - : base(loggerFactory, jsonApiContext, contextResolver, resourceDefinition) - { } - } - - /// /// Provides a default repository implementation and is responsible for /// abstracting any EF Core APIs away from the service layer. @@ -148,7 +125,6 @@ public virtual IQueryable Sort(IQueryable entities, List where TEntity : class, IIdentifiable { } - public interface IGuidEntityReadRepository - : IEntityReadRepository - where TEntity : class, IIdentifiable - { } - public interface IEntityReadRepository where TEntity : class, IIdentifiable { diff --git a/src/JsonApiDotNetCore/Data/IEntityRepository.cs b/src/JsonApiDotNetCore/Data/IEntityRepository.cs index d9afdae927..d4e8870341 100644 --- a/src/JsonApiDotNetCore/Data/IEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/IEntityRepository.cs @@ -3,10 +3,6 @@ namespace JsonApiDotNetCore.Data { - public interface IGuidEntityRepository - : IEntityRepository - where TEntity : class, IIdentifiable - { } public interface IEntityRepository : IEntityRepository diff --git a/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs b/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs index 389fb845aa..02ed9a9f50 100644 --- a/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs +++ b/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs @@ -10,11 +10,6 @@ public interface IEntityWriteRepository where TEntity : class, IIdentifiable { } - public interface IGuidEntityWriteRepository - : IEntityReadRepository - where TEntity : class, IIdentifiable - { } - public interface IEntityWriteRepository where TEntity : class, IIdentifiable { diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index 687ca2e8a0..61450326cf 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -112,9 +112,13 @@ public static void AddJsonApiInternals( AddOperationServices(services); services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>)); - services.AddScoped(typeof(IGuidEntityRepository<>), typeof(DefaultGuidEntityRepository<>)); services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>)); + services.AddScoped(typeof(IEntityReadRepository<,>), typeof(DefaultEntityRepository<,>)); + services.AddScoped(typeof(IEntityWriteRepository<,>), typeof(DefaultEntityRepository<,>)); + + + services.AddScoped(typeof(ICreateService<>), typeof(EntityResourceService<>)); services.AddScoped(typeof(ICreateService<,>), typeof(EntityResourceService<,>)); diff --git a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs index cec60a95d3..99c3845108 100644 --- a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs +++ b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs @@ -40,13 +40,10 @@ public class ServiceDiscoveryFacade internal static HashSet RepositoryInterfaces = new HashSet { typeof(IEntityRepository<>), - typeof(IGuidEntityRepository<>), typeof(IEntityRepository<,>), typeof(IEntityWriteRepository<>), - typeof(IGuidEntityWriteRepository<>), typeof(IEntityWriteRepository<,>), typeof(IEntityReadRepository<>), - typeof(IGuidEntityReadRepository<>), typeof(IEntityReadRepository<,>) }; diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index 134627c0de..159b219ca9 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -165,8 +165,7 @@ IEnumerable GetWhereAndInclude(IEnumerable ids, stri IEntityReadRepository GetRepository() where TEntity : class, IIdentifiable { - var openType = typeof(TId) == typeof(Guid) ? typeof(IGuidEntityRepository<>) : typeof(IEntityRepository<>); - return _genericProcessorFactory.GetProcessor>(openType, typeof(TEntity)); + return _genericProcessorFactory.GetProcessor>(typeof(IEntityReadRepository<,>), typeof(TEntity), typeof(TId)); } diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 6fc6baa639..aa2d5a7116 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -325,12 +325,12 @@ void SetupProcessorFactoryForResourceDefinition( if (dbContext != null) { - var repo = CreateTestRepository(dbContext, apiContext); - processorFactory.Setup(c => c.GetProcessor>(typeof(IEntityRepository<>), typeof(TModel))).Returns(repo); + IEntityReadRepository repo = CreateTestRepository(dbContext, apiContext); + processorFactory.Setup(c => c.GetProcessor>(typeof(IEntityReadRepository<,>), typeof(TModel), typeof(int))).Returns(repo); } } - IEntityRepository CreateTestRepository( + IEntityReadRepository CreateTestRepository( AppDbContext dbContext, IJsonApiContext apiContext ) where TModel : class, IIdentifiable From c70dea4376df3dd80c0a9a9ce227250ab0b07eae Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 12:32:29 +0200 Subject: [PATCH 152/168] documentation: enumeration change --- docs/usage/resources/hooks.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index 68c16f5064..8ed7086c59 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -1,4 +1,4 @@ - +f # Resource Hooks This section covers the usage of **Resource Hooks**, which is a feature of`ResourceDefinition`. See the [ResourceDefinition usage guide](resource-definitions.md) for a general explanation on how to set up a `ResourceDefinition`. For a quick start, jump right to the [Getting started: most minimal example](#getting-started-most-minimal-example) section. @@ -369,8 +369,8 @@ Now let's consider an API user that tries to update `New Article` by setting its 2. Is the API user allowed to update `Alice`? Apart from this, we also wish to verify permissions for the resources that are **implicitly affected** by this operation: `Bob` and `Old Article`. Setting `Alice` as the new author of `New Article` will result in removing the following two relationships: `Bob` being an author of `New Article`, and `Alice` being an author of `Old Article`. Therefore, we wish wish to verify the related permissions: -1. Is the API user allowed to update `Bob`? -2. Is the API user allowed to update `Old Article`? +3. Is the API user allowed to update `Bob`? +4. Is the API user allowed to update `Old Article`? This authorization requirement can be fulfilled as follows. From e184e7f874c948b938576cf6d567e8956eadc15d Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 12:34:08 +0200 Subject: [PATCH 153/168] documentation: typo --- docs/usage/resources/hooks.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index 8ed7086c59..ad13907f65 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -1,4 +1,4 @@ -f + # Resource Hooks This section covers the usage of **Resource Hooks**, which is a feature of`ResourceDefinition`. See the [ResourceDefinition usage guide](resource-definitions.md) for a general explanation on how to set up a `ResourceDefinition`. For a quick start, jump right to the [Getting started: most minimal example](#getting-started-most-minimal-example) section. @@ -369,6 +369,7 @@ Now let's consider an API user that tries to update `New Article` by setting its 2. Is the API user allowed to update `Alice`? Apart from this, we also wish to verify permissions for the resources that are **implicitly affected** by this operation: `Bob` and `Old Article`. Setting `Alice` as the new author of `New Article` will result in removing the following two relationships: `Bob` being an author of `New Article`, and `Alice` being an author of `Old Article`. Therefore, we wish wish to verify the related permissions: + 3. Is the API user allowed to update `Bob`? 4. Is the API user allowed to update `Old Article`? From 61dc5d53a352aa564f8e0ad15d33d01cfd401e9b Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 12:37:29 +0200 Subject: [PATCH 154/168] fix: remove unused code --- .../Data/DefaultEntityRepository.cs | 31 ------------------- .../Data/IEntityReadRepository.cs | 1 - 2 files changed, 32 deletions(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 377b330cac..b15746e7d4 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -210,37 +210,6 @@ public void DetachRelationshipPointers(TEntity entity) } } - /// - /// the constraint means it can be a to one or to many, but not hasmanythrough - /// - /// true, if inverse was attached, false otherwise. - /// Entity. - /// Relationship. - /// The 1st type parameter. - public virtual TPrincipal AttachInverse(TEntity entity, RelationshipAttribute relationship) where TPrincipal : class, IIdentifiable - { - if (relationship is HasManyThroughAttribute) return null; - - var entityMeta = _context.Model.FindEntityType(typeof(TPrincipal)); - INavigation inverseNavigation = entityMeta.FindNavigation(relationship.InternalRelationshipName).FindInverse(); - - if (inverseNavigation != null) - { - //TODO: need to make sure we're not reattaching - entity = (TEntity)((IEnumerable)PreventReattachment(new List { entity })).Single(); - var entityEntry = _context.Attach(entity); - entityEntry.Reload(); // TODO: we should only reload if the involved foreign key value is null. - entityEntry.Reference(inverseNavigation.Name).Load(); - - return (TPrincipal)inverseNavigation.PropertyInfo.GetValue(entity); - - } - return null; - - } - - - /// /// This is used to allow creation of HasMany relationships when the /// dependent side of the relationship already exists. diff --git a/src/JsonApiDotNetCore/Data/IEntityReadRepository.cs b/src/JsonApiDotNetCore/Data/IEntityReadRepository.cs index 3e8f65f840..bcbcdb9f8e 100644 --- a/src/JsonApiDotNetCore/Data/IEntityReadRepository.cs +++ b/src/JsonApiDotNetCore/Data/IEntityReadRepository.cs @@ -82,6 +82,5 @@ public interface IEntityReadRepository /// Convert the collection to a materialized list /// Task> ToListAsync(IQueryable entities); - TPrincipal AttachInverse(TEntity entity, RelationshipAttribute relationship) where TPrincipal : class, IIdentifiable; } } From 50167e09d884b2eefceba625c632316540cab629 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 12:47:17 +0200 Subject: [PATCH 155/168] documentation: improve advanced auth example --- docs/usage/resources/hooks.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index ad13907f65..6922880cb2 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -364,7 +364,9 @@ Let's consider an authorization scenario for which we are required to implement * The author of article `Old Article` is person `Alice`. * The author of article `New Article` is person `Bob`. -Now let's consider an API user that tries to update `New Article` by setting its author to `Alice`. First to all, we wish to authorize this operation by the verifying permissions related to the resources that are **explicity affected** by it: +Now let's consider an API user that tries to update `New Article` by setting its author to `Alice`. The request would look something like `PATCH /articles/{NewArticleId}` with a body containing a reference to `Alice`. + +First to all, we wish to authorize this operation by the verifying permissions related to the resources that are **explicity affected** by it: 1. Is the API user allowed to update `New Article`? 2. Is the API user allowed to update `Alice`? From 61634f1b0f85f96eb5518188d5bd3aeaa7205318 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 13:41:09 +0200 Subject: [PATCH 156/168] fix: reverted change of 2c6f751 --- src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs index 6f61a975c7..a5b7c6788c 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs @@ -151,11 +151,11 @@ private object SetEntityAttributes( { if (attributeValues.TryGetValue(attr.PublicAttributeName, out object newValue)) { - if (attr.IsImmutable) - continue; var convertedValue = ConvertAttrValue(newValue, attr.PropertyInfo.PropertyType); attr.SetValue(entity, convertedValue); - _jsonApiContext.AttributesToUpdate[attr] = convertedValue; + + if (attr.IsImmutable == false) + _jsonApiContext.AttributesToUpdate[attr] = convertedValue; } } From a1f5d12db53d66433fbdeb57f6e6136ca4582f5a Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 16:43:31 +0200 Subject: [PATCH 157/168] feat: more structual ctors EntityResourceService --- .../Services/EntityResourceService.cs | 54 +++++++++---------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 55832ca78c..2b4a1d79be 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -47,51 +47,47 @@ public class EntityResourceService : private readonly IResourceMapper _mapper; private readonly IResourceHookExecutor _hookExecutor; + public EntityResourceService( - IJsonApiContext jsonApiContext, - IEntityRepository entityRepository, - IResourceHookExecutor hookExecutor, - ILoggerFactory loggerFactory = null) + IJsonApiContext jsonApiContext, + IEntityRepository entityRepository, + IResourceHookExecutor hookExecutor = null, + IResourceMapper mapper = null, + ILoggerFactory loggerFactory = null) { - // no mapper provided, TResource & TEntity must be the same type - if (typeof(TResource) != typeof(TEntity)) + _jsonApiContext = jsonApiContext; + _entities = entityRepository; + + if (mapper == null && typeof(TResource) != typeof(TEntity)) { throw new InvalidOperationException("Resource and Entity types are NOT the same. Please provide a mapper."); } - - _jsonApiContext = jsonApiContext; - _entities = entityRepository; _hookExecutor = hookExecutor; + _mapper = mapper; _logger = loggerFactory?.CreateLogger>(); } + + public EntityResourceService( + IJsonApiContext jsonApiContext, + IEntityRepository entityRepository, + IResourceHookExecutor hookExecutor, + ILoggerFactory loggerFactory = null) : this(jsonApiContext, entityRepository, hookExecutor: hookExecutor, mapper: null, loggerFactory: null) + { } + public EntityResourceService( IJsonApiContext jsonApiContext, IEntityRepository entityRepository, - ILoggerFactory loggerFactory = null) - { - // no mapper provided, TResource & TEntity must be the same type - if (typeof(TResource) != typeof(TEntity)) - { - throw new InvalidOperationException("Resource and Entity types are NOT the same. Please provide a mapper."); - } - - _jsonApiContext = jsonApiContext; - _entities = entityRepository; - _logger = loggerFactory?.CreateLogger>(); - } + ILoggerFactory loggerFactory = null) : this(jsonApiContext, entityRepository, hookExecutor: null, mapper: null, loggerFactory: loggerFactory) + { } + [Obsolete("Use ctor with signature (jsonApiContext, entityRepository, hookExecutor = null, mapper = null, loggerFactory = null")] public EntityResourceService( IJsonApiContext jsonApiContext, IEntityRepository entityRepository, - IResourceMapper mapper, - ILoggerFactory loggerFactory = null) - { - _jsonApiContext = jsonApiContext; - _entities = entityRepository; - _logger = loggerFactory.CreateLogger>(); - _mapper = mapper; - } + ILoggerFactory loggerFactory, + IResourceMapper mapper ) : this(jsonApiContext, entityRepository, hookExecutor: null, mapper: mapper, loggerFactory: loggerFactory) + { } public virtual async Task CreateAsync(TResource resource) { From b8df7494b3b6dc09890907b2159e4950e1180fee Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 18:37:54 +0200 Subject: [PATCH 158/168] feat: improved loaddbvalues attribute error handling, refactored a bit --- .../Data/DefaultEntityRepository.cs | 15 +--- .../Data/IEntityWriteRepository.cs | 2 - .../Hooks/Discovery/HooksDiscovery.cs | 71 +++++++++++-------- .../Hooks/Discovery/IHooksDiscovery.cs | 4 +- .../Hooks/Execution/HookExecutorHelper.cs | 25 +++---- .../Hooks/Execution/IHookExecutorHelper.cs | 7 +- .../Execution/UpdatedRelationshipHelper.cs | 7 +- .../Hooks/ResourceHookExecutor.cs | 14 +++- .../Services/EntityResourceService.cs | 5 +- .../UnitTests/ResourceHooks/DiscoveryTests.cs | 47 ++++++++++++ .../ResourceHooks/ResourceHooksTestsSetup.cs | 8 +-- 11 files changed, 129 insertions(+), 76 deletions(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index b15746e7d4..d31eabfb21 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -10,7 +10,6 @@ using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.Logging; namespace JsonApiDotNetCore.Data { @@ -378,22 +377,14 @@ public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute await genericProcessor.UpdateRelationshipsAsync(parent, relationship, relationshipIds); } + /// public virtual async Task DeleteAsync(TId id) { - return await DeleteAsync(await GetAsync(id)); - - } - - public virtual async Task DeleteAsync(TEntity entity) - { - if (entity == null) - return false; - - + var entity = await GetAsync(id); + if (entity == null) return false; _dbSet.Remove(entity); await _context.SaveChangesAsync(); - return true; } diff --git a/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs b/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs index 02ed9a9f50..1e02786df3 100644 --- a/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs +++ b/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs @@ -19,8 +19,6 @@ public interface IEntityWriteRepository Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds); - Task DeleteAsync(TEntity entity); - Task DeleteAsync(TId id); } } diff --git a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs index 5e19da4392..958a3e3ab2 100644 --- a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs +++ b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs @@ -14,11 +14,16 @@ namespace JsonApiDotNetCore.Hooks public class HooksDiscovery : IHooksDiscovery where TEntity : class, IIdentifiable { private readonly ResourceHook[] _allHooks; - + private readonly ResourceHook[] _databaseValuesAttributeAllowed = + { + ResourceHook.BeforeUpdate, + ResourceHook.BeforeUpdateRelationship, + ResourceHook.BeforeDelete + }; /// public ResourceHook[] ImplementedHooks { get; private set; } - public ResourceHook[] DatabaseDiffEnabledHooks { get; private set; } - public ResourceHook[] DatabaseDiffDisabledHooks { get; private set; } + public ResourceHook[] DatabaseValuesEnabledHooks { get; private set; } + public ResourceHook[] DatabaseValuesDisabledHooks { get; private set; } public HooksDiscovery() @@ -38,43 +43,47 @@ void DiscoverImplementedHooksForModel() { Type parameterizedResourceDefinition = typeof(ResourceDefinition); var derivedTypes = TypeLocator.GetDerivedTypes(typeof(TEntity).Assembly, parameterizedResourceDefinition).ToList(); + + + var implementedHooks = new List(); + var enabledHooks = new List() { ResourceHook.BeforeImplicitUpdateRelationship } ; + var disabledHooks = new List(); + Type targetType = null; try { - var implementedHooks = new List(); - var diffEnabledHooks = new List(); - var diffDisabledHooks = new List(); - Type targetType = derivedTypes.SingleOrDefault(); // multiple containers is not supported - if (targetType != null) + targetType = derivedTypes.SingleOrDefault(); // multiple containers is not supported + } + catch + { + throw new JsonApiSetupException($"It is currently not supported to" + + "implement hooks across multiple implementations of ResourceDefinition"); + } + if (targetType != null) + { + foreach (var hook in _allHooks) { - foreach (var hook in _allHooks) + var method = targetType.GetMethod(hook.ToString("G")); + if (method.DeclaringType != parameterizedResourceDefinition) { - var method = targetType.GetMethod(hook.ToString("G")); - if (method.DeclaringType != parameterizedResourceDefinition) + implementedHooks.Add(hook); + var attr = method.GetCustomAttributes(true).OfType().SingleOrDefault(); + if (attr != null) { - implementedHooks.Add(hook); - if (hook == ResourceHook.BeforeImplicitUpdateRelationship) - { - diffEnabledHooks.Add(hook); - continue; - } - var attr = method.GetCustomAttributes(true).OfType().SingleOrDefault(); - if (attr != null) + if (!_databaseValuesAttributeAllowed.Contains(hook)) { - var targetList = attr.value ? diffEnabledHooks : diffDisabledHooks; - targetList.Add(hook); + throw new JsonApiSetupException($"DatabaseValuesAttribute cannot be used on hook" + + $"{hook.ToString("G")} in resource definition {parameterizedResourceDefinition.Name}"); } - } - } - + var targetList = attr.value ? enabledHooks : disabledHooks; + targetList.Add(hook); + } + } } - ImplementedHooks = implementedHooks.ToArray(); - DatabaseDiffDisabledHooks = diffDisabledHooks.ToArray(); - DatabaseDiffEnabledHooks = diffEnabledHooks.ToArray(); - } catch (Exception e) - { - throw new JsonApiSetupException($@"Incorrect resource hook setup. For a given model of type TEntity, - only one class may implement IResourceHookContainer"); + } + ImplementedHooks = implementedHooks.ToArray(); + DatabaseValuesDisabledHooks = disabledHooks.ToArray(); + DatabaseValuesEnabledHooks = enabledHooks.ToArray(); } } diff --git a/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs b/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs index c0e342e4e9..709b30900e 100644 --- a/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs +++ b/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs @@ -21,8 +21,8 @@ public interface IHooksDiscovery /// /// The implemented hooks. ResourceHook[] ImplementedHooks { get; } - ResourceHook[] DatabaseDiffEnabledHooks { get; } - ResourceHook[] DatabaseDiffDisabledHooks { get; } + ResourceHook[] DatabaseValuesEnabledHooks { get; } + ResourceHook[] DatabaseValuesDisabledHooks { get; } } } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index 159b219ca9..68b8018c01 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -29,7 +29,7 @@ internal class HookExecutorHelper : IHookExecutorHelper public HookExecutorHelper( IGenericProcessorFactory genericProcessorFactory, IResourceGraph graph, - IJsonApiContext context = null + IJsonApiContext context ) { _genericProcessorFactory = genericProcessorFactory; @@ -81,39 +81,38 @@ public IResourceHookContainer GetResourceHookContainer(Resourc return (IResourceHookContainer)GetResourceHookContainer(typeof(TEntity), hook); } - public IEnumerable LoadDbValues(PrincipalType repositoryEntityType, Type affectedHookEntityType, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships) + public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships) { - if (!ShouldLoadDbValues(affectedHookEntityType, hook)) return null; - var paths = relationships.Select(p => p.Attribute.RelationshipPath).ToArray(); - var idType = GetIdentifierType(repositoryEntityType); + var idType = GetIdentifierType(entityTypeForRepository); var parameterizedGetWhere = GetType() .GetMethod(nameof(GetWhereAndInclude), BindingFlags.NonPublic | BindingFlags.Instance) - .MakeGenericMethod(repositoryEntityType, idType); + .MakeGenericMethod(entityTypeForRepository, idType); var casted = ((IEnumerable)entities).Cast(); var ids = casted.Select(e => e.StringId).Cast(idType); var values = (IEnumerable)parameterizedGetWhere.Invoke(this, new object[] { ids, paths }); - return values; + if (values == null) return null; + return (IEnumerable)Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(entityTypeForRepository), values.Cast(entityTypeForRepository)); } public HashSet LoadDbValues(IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships) where TEntity : class, IIdentifiable { var entityType = typeof(TEntity); - var dbValues = LoadDbValues(entityType, entityType, entities, hook, relationships)?.Cast(); + var dbValues = LoadDbValues(entityType, entities, hook, relationships)?.Cast(); if (dbValues == null) return null; return new HashSet(dbValues); } - bool ShouldLoadDbValues(DependentType entityType, ResourceHook hook) + public bool ShouldLoadDbValues(Type entityType, ResourceHook hook) { var discovery = GetHookDiscovery(entityType); - if (discovery.DatabaseDiffDisabledHooks.Contains(hook)) + if (discovery.DatabaseValuesDisabledHooks.Contains(hook)) { return false; } - else if (discovery.DatabaseDiffEnabledHooks.Contains(hook)) + else if (discovery.DatabaseValuesEnabledHooks.Contains(hook)) { return true; } @@ -177,7 +176,9 @@ public Dictionary LoadImplicitlyAffected( foreach (var kvp in principalEntitiesByRelation) { if (IsHasManyThrough(kvp, out var principals, out var relationship)) continue; - var includedPrincipals = LoadDbValues(relationship.PrincipalType, relationship.DependentType, principals, ResourceHook.BeforeImplicitUpdateRelationship, relationship); + + // 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); foreach (IIdentifiable ip in includedPrincipals) { diff --git a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs index 5e2f4f5e6f..33546aa864 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs @@ -42,10 +42,7 @@ internal interface IHookExecutorHelper /// /// For a set of entities, loads current values from the database /// - IEnumerable LoadDbValues(Type repositoryEntityType, Type affectedHookEntityType, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships); - /// - /// For a set of entities, loads current values from the database - /// - HashSet LoadDbValues(IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships) where TEntity : class, IIdentifiable; + IEnumerable LoadDbValues(Type repositoryEntityType, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships); + bool ShouldLoadDbValues(Type containerEntityType, ResourceHook hook); } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs index 58535d68e8..4882c72514 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs @@ -16,7 +16,8 @@ public interface IAffectedRelationships : IAffectedRelationships whe /// /// Gets a dictionary of all entities grouped by affected relationship. /// - Dictionary> All { get; } + Dictionary> AllByRelationships(); + /// /// Gets a dictionary of all entities that have an affected relationship to type /// @@ -31,9 +32,9 @@ public class UpdatedRelationshipHelper : IAffectedRelationships> _groups; - public Dictionary> All + public Dictionary> AllByRelationships() { - get { return _groups?.ToDictionary(p => p.Key.Attribute, p => p.Value); } + return _groups?.ToDictionary(p => p.Key.Attribute, p => p.Value); } public UpdatedRelationshipHelper(Dictionary relationships) { diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index 42ffe18754..93c7a6f9d9 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -47,7 +47,7 @@ public virtual IEnumerable BeforeUpdate(IEnumerable e { if (GetHook(ResourceHook.BeforeUpdate, entities, out var container, out var node)) { - var dbValues = _executorHelper.LoadDbValues((IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, node.RelationshipsToNextLayer); + var dbValues = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, node.RelationshipsToNextLayer); var diff = new EntityDiff(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer()); IEnumerable updated = container.BeforeUpdate(diff, pipeline); node.UpdateUnique(updated); @@ -58,6 +58,7 @@ public virtual IEnumerable BeforeUpdate(IEnumerable e return entities; } + /// public virtual IEnumerable BeforeCreate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable { @@ -77,7 +78,8 @@ public virtual IEnumerable BeforeDelete(IEnumerable e { if (GetHook(ResourceHook.BeforeDelete, entities, out var container, out var node)) { - IEnumerable updated = container.BeforeDelete((HashSet)node.UniqueEntities, pipeline); + var targetEntities = (LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, node.RelationshipsToNextLayer) ?? node.UniqueEntities); + IEnumerable updated = container.BeforeDelete((HashSet)targetEntities, pipeline); node.UpdateUnique(updated); node.Reassign(entities); } @@ -246,7 +248,7 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, EntityChildLayer lay { if (uniqueEntities.Cast().Any()) { - var dbValues = _executorHelper.LoadDbValues(entityType, entityType, uniqueEntities, ResourceHook.BeforeUpdateRelationship, node.RelationshipsToNextLayer); + var dbValues = LoadDbValues(entityType, uniqueEntities, ResourceHook.BeforeUpdateRelationship, node.RelationshipsToNextLayer); var resourcesByRelationship = CreateRelationshipHelper(entityType, node.RelationshipsFromPreviousLayer.GetDependentEntities(), dbValues); var allowedIds = CallHook(nestedHookcontainer, ResourceHook.BeforeUpdateRelationship, new object[] { GetIds(uniqueEntities), resourcesByRelationship, pipeline }).Cast(); var updated = GetAllowedEntities(uniqueEntities, allowedIds); @@ -381,6 +383,12 @@ RelationshipProxy GetInverseRelationship(RelationshipProxy proxy) return new RelationshipProxy(_graph.GetInverseRelationship(proxy.Attribute), proxy.PrincipalType, false); } + IEnumerable LoadDbValues(Type containerEntityType, IEnumerable uniqueEntities, ResourceHook targetHook, RelationshipProxy[] relationshipsToNextLayer) + { + if (!_executorHelper.ShouldLoadDbValues(containerEntityType, targetHook)) return null; + return _executorHelper.LoadDbValues(containerEntityType, uniqueEntities, targetHook, relationshipsToNextLayer); + } + void FireAfterUpdateRelationship(IResourceHookContainer container, IEntityNode node, ResourcePipeline pipeline) { var resourcesByRelationship = CreateRelationshipHelper(node.EntityType, node.RelationshipsFromPreviousLayer.GetDependentEntities()); diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 2b4a1d79be..88057c10d1 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -116,9 +116,10 @@ public virtual async Task CreateAsync(TResource resource) } public virtual async Task DeleteAsync(TId id) { - var entity = await _entities.GetAsync(id); + var entity = (TEntity)Activator.CreateInstance(typeof(TEntity)); + entity.Id = id; if (!IsNull(_hookExecutor, entity)) _hookExecutor.BeforeDelete(AsList(entity), ResourcePipeline.Delete); - var succeeded = await _entities.DeleteAsync(entity); + var succeeded = await _entities.DeleteAsync(entity.Id); if (!IsNull(_hookExecutor, entity)) _hookExecutor.AfterDelete(AsList(entity), ResourcePipeline.Delete, succeeded); return succeeded; } diff --git a/test/UnitTests/ResourceHooks/DiscoveryTests.cs b/test/UnitTests/ResourceHooks/DiscoveryTests.cs index 9978d52dd4..9fd3a6305b 100644 --- a/test/UnitTests/ResourceHooks/DiscoveryTests.cs +++ b/test/UnitTests/ResourceHooks/DiscoveryTests.cs @@ -52,5 +52,52 @@ public void Hook_Discovery_With_Inheritance() Assert.Contains(ResourceHook.BeforeDelete, hookConfig.ImplementedHooks); Assert.Contains(ResourceHook.AfterDelete, hookConfig.ImplementedHooks); } + + + public class YetAnotherDummy : Identifiable { } + public class YetAnotherDummyResourceDefinition : ResourceDefinition + { + public YetAnotherDummyResourceDefinition() : base(new ResourceGraphBuilder().AddResource().Build()) { } + + public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + + [LoadDatabaseValues(false)] + public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } + } + [Fact] + public void LoadDatabaseValues_Attribute_Not_Allowed() + { + // assert + Assert.Throws(() => + { + // arrange & act + var hookConfig = new HooksDiscovery(); + }); + + } + + public class DoubleDummy : Identifiable { } + public class DoubleDummyResourceDefinition1 : ResourceDefinition + { + public DoubleDummyResourceDefinition1() : base(new ResourceGraphBuilder().AddResource().Build()) { } + + public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + } + public class DoubleDummyResourceDefinition2 : ResourceDefinition + { + public DoubleDummyResourceDefinition2() : base(new ResourceGraphBuilder().AddResource().Build()) { } + + public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } + } + [Fact] + public void Multiple_Implementations_Of_ResourceDefinitions() + { + // assert + Assert.Throws(() => + { + // arrange & act + var hookConfig = new HooksDiscovery(); + }); + } } } diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index aa2d5a7116..c26968511f 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -146,7 +146,7 @@ public class HooksTestsSetup : HooksDummyData // wiring up the mocked GenericProcessorFactory to return the correct resource definition SetupProcessorFactoryForResourceDefinition(processorFactory, mainResource.Object, discovery, context.Object); - var meta = new HookExecutorHelper(context.Object.GenericProcessorFactory, ResourceGraph.Instance); + var meta = new HookExecutorHelper(context.Object.GenericProcessorFactory, ResourceGraph.Instance, context.Object); var hookExecutor = new ResourceHookExecutor(meta, context.Object, ResourceGraph.Instance); return (context, hookExecutor, mainResource); @@ -202,7 +202,7 @@ public class HooksTestsSetup : HooksDummyData var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; SetupProcessorFactoryForResourceDefinition(processorFactory, mainResource.Object, mainDiscovery, context.Object, dbContext); - var meta = new HookExecutorHelper(context.Object.GenericProcessorFactory, ResourceGraph.Instance); + var meta = new HookExecutorHelper(context.Object.GenericProcessorFactory, ResourceGraph.Instance, context.Object); var hookExecutor = new ResourceHookExecutor(meta, context.Object, ResourceGraph.Instance); SetupProcessorFactoryForResourceDefinition(processorFactory, firstNestedResource.Object, firstNestedDiscovery, context.Object, dbContext); @@ -220,10 +220,10 @@ protected IHooksDiscovery SetDiscoverableHooks(ResourceHook[] if (!enableDbValuesHooks.Any()) { - mock.Setup(discovery => discovery.DatabaseDiffDisabledHooks) + mock.Setup(discovery => discovery.DatabaseValuesDisabledHooks) .Returns(enableDbValuesHooks); } - mock.Setup(discovery => discovery.DatabaseDiffEnabledHooks) + mock.Setup(discovery => discovery.DatabaseValuesEnabledHooks) .Returns(new ResourceHook[] { ResourceHook.BeforeImplicitUpdateRelationship }.Concat(enableDbValuesHooks).ToArray()); return mock.Object; From 29abae026daae15db2fc2fbd202b88d16e16a46f Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 18:49:59 +0200 Subject: [PATCH 159/168] refactor: renamed AffectedRelationships --- ...{UpdatedRelationshipHelper.cs => AffectedRelationships.cs} | 4 ++-- src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs | 2 +- src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/JsonApiDotNetCore/Hooks/Execution/{UpdatedRelationshipHelper.cs => AffectedRelationships.cs} (90%) diff --git a/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs similarity index 90% rename from src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs rename to src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs index 4882c72514..ac6a3f2541 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/UpdatedRelationshipHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs @@ -28,7 +28,7 @@ public interface IAffectedRelationships : IAffectedRelationships whe Dictionary> GetByRelationship(Type principalType); } - public class UpdatedRelationshipHelper : IAffectedRelationships where TDependent : class, IIdentifiable + public class AffectedRelationships : IAffectedRelationships where TDependent : class, IIdentifiable { private readonly Dictionary> _groups; @@ -36,7 +36,7 @@ public Dictionary> AllByRelationships { return _groups?.ToDictionary(p => p.Key.Attribute, p => p.Value); } - public UpdatedRelationshipHelper(Dictionary relationships) + public AffectedRelationships(Dictionary relationships) { _groups = relationships.ToDictionary(kvp => kvp.Key, kvp => new HashSet((IEnumerable)kvp.Value)); } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs index af57ff048d..68945bc409 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs @@ -19,7 +19,7 @@ public interface IEntityDiff : IAffectedRelationships where TE HashSet DatabaseEntities { get; } } - public class EntityDiff : UpdatedRelationshipHelper, IEntityDiff where TEntity : class, IIdentifiable + public class EntityDiff : AffectedRelationships, IEntityDiff where TEntity : class, IIdentifiable { public HashSet RequestEntities { get; private set; } public HashSet DatabaseEntities { get; private set; } diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index 93c7a6f9d9..50d3f20caf 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -343,14 +343,14 @@ object ThrowJsonApiExceptionOnError(Func action) } /// - /// Helper method to instantiate UpdatedRelationshipHelper for a given + /// Helper method to instantiate AffectedRelationships for a given /// If are included, the values of the entries in need to be replaced with these values. /// /// The relationship helper. IAffectedRelationships CreateRelationshipHelper(DependentType entityType, Dictionary prevLayerRelationships, IEnumerable dbValues = null) { if (dbValues != null) ReplaceWithDbValues(prevLayerRelationships, dbValues.Cast()); - return (IAffectedRelationships)TypeHelper.CreateInstanceOfOpenType(typeof(UpdatedRelationshipHelper<>), entityType, prevLayerRelationships); + return (IAffectedRelationships)TypeHelper.CreateInstanceOfOpenType(typeof(AffectedRelationships<>), entityType, prevLayerRelationships); } /// From 8fc015adcd6762b98218dca4a4816df63e3869af Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 5 Jun 2019 19:05:28 +0200 Subject: [PATCH 160/168] feat: throw error if trying to access databasevalues when loading is turned off --- .../Hooks/Execution/EntityDiff.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs index 68945bc409..d48f3dbcee 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs @@ -1,10 +1,14 @@  +using System; using System.Collections; using System.Collections.Generic; +using System.Linq; +using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Hooks { + /// /// A helper class that provides insight in what is to be updated. The /// property reflects what was parsed from the incoming request, @@ -21,14 +25,21 @@ public interface IEntityDiff : IAffectedRelationships where TE public class EntityDiff : AffectedRelationships, IEntityDiff where TEntity : class, IIdentifiable { + private readonly HashSet _databaseEntities; + public HashSet DatabaseEntities { get => _databaseEntities ?? ThrowNoDbValuesError(); } + public HashSet RequestEntities { get; private set; } - public HashSet DatabaseEntities { get; private set; } public EntityDiff(IEnumerable requestEntities, IEnumerable databaseEntities, - Dictionary relationships) : base (relationships) + Dictionary relationships) : base(relationships) { RequestEntities = (HashSet)requestEntities; - DatabaseEntities = (HashSet)databaseEntities; + _databaseEntities = (HashSet)databaseEntities; + } + + private HashSet ThrowNoDbValuesError() + { + throw new MemberAccessException("Cannot access database entities if the LoadDatabaseValues option is set to false"); } } } From 59a93590ac4f05c9c246eca9459b49e331250805 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 6 Jun 2019 11:05:30 +0200 Subject: [PATCH 161/168] feat: IAffectedResource, IEntityDiff, IAffectedRelationships have upgraded for better quality of life --- .../Resources/PassportResource.cs | 4 +- .../Resources/PersonResource.cs | 6 ++ .../Hooks/Execution/AffectedRelationships.cs | 2 +- .../Hooks/Execution/AffectedResources.cs | 57 +++++++++++++++++++ .../Hooks/Execution/EntityDiff.cs | 45 +++++++++++---- .../Hooks/IResourceHookContainer.cs | 6 +- .../Hooks/ResourceHookExecutor.cs | 11 ++-- src/JsonApiDotNetCore/Internal/TypeHelper.cs | 21 +++++++ .../Models/ResourceDefinition.cs | 6 +- .../Acceptance/Spec/UpdatingDataTests.cs | 7 +++ .../UnitTests/ResourceHooks/DiscoveryTests.cs | 8 +-- .../Create/BeforeCreateTests.cs | 6 +- .../Create/BeforeCreate_WithDbValues_Tests.cs | 8 +-- .../Delete/BeforeDeleteTests.cs | 2 +- .../Delete/BeforeDelete_WithDbValue_Tests.cs | 4 +- .../Update/BeforeUpdateTests.cs | 4 +- .../Update/BeforeUpdate_WithDbValues_Tests.cs | 14 ++--- .../ResourceHooks/ResourceHooksTestsSetup.cs | 8 +-- 18 files changed, 168 insertions(+), 51 deletions(-) create mode 100644 src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs index 9955997d8b..b1a86bf4b8 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs @@ -14,9 +14,9 @@ public PassportResource(IResourceGraph graph) : base(graph) { } - public override void BeforeRead(ResourcePipeline pipeline, bool nestedHook = false, string stringId = null) + public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { - if (pipeline == ResourcePipeline.GetSingle && nestedHook) + if (pipeline == ResourcePipeline.GetSingle && isIncluded) { throw new JsonApiException(403, "Not allowed to include passports on individual people", new UnauthorizedAccessException()); } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs index 191a70a89a..ec6b4458ab 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs @@ -16,6 +16,12 @@ public override IEnumerable BeforeUpdateRelationship(HashSet ids return ids; } + //[LoadDatabaseValues(true)] + //public override IEnumerable BeforeUpdate(IEntityDiff entityDiff, ResourcePipeline pipeline) + //{ + // return entityDiff.Entities; + //} + public override void BeforeImplicitUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { resourcesByRelationship.GetByRelationship().ToList().ForEach(kvp => DisallowLocked(kvp.Value)); diff --git a/src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs b/src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs index ac6a3f2541..dc2590f9bd 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs @@ -36,7 +36,7 @@ public Dictionary> AllByRelationships { return _groups?.ToDictionary(p => p.Key.Attribute, p => p.Value); } - public AffectedRelationships(Dictionary relationships) + internal AffectedRelationships(Dictionary relationships) { _groups = relationships.ToDictionary(kvp => kvp.Key, kvp => new HashSet((IEnumerable)kvp.Value)); } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs new file mode 100644 index 0000000000..63c1d503c1 --- /dev/null +++ b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using JsonApiDotNetCore.Models; +using System.Linq; +using System.Collections; + +namespace JsonApiDotNetCore.Hooks +{ + + public interface IAffectedResources : IAffectedResourcesBase, IEnumerable where TEntity : class, IIdentifiable + { + + } + + /// NOTE: you might wonder why there is a separate AffectedResourceBase and AffectedResource. + /// If we merge them together, ie get rid of the base and just let the AffectedResource directly implement IEnumerable{TEntity}, + /// we will run in to problems with the following: + /// EntityDiff{} inherits from AffectedResource{TEntity}, + /// but EntityDiff also implements IEnumerable{EntityDiffPair{TEntity}}. This means that + /// EntityDiff will implement two IEnumerable{x} where (x1 = TEntity and x2 = EntityDiffPair{TEntity} ) + /// The problem with this is that when you then try to do a simple foreach loop over + /// a EntityDiff instance, it will throw an error, because it does not know which of the two enumerators to pick. + /// We want EntityDiff to only loop over the EntityDiffPair, so we can do that by making sure + /// it doesn't inherit the IEnumerable{TEntity} part from AffectedResources. + public interface IAffectedResourcesBase where TEntity : class, IIdentifiable + { + HashSet Entities { get; } + } + + public class AffectedResources : AffectedResourcesBase, IAffectedResources where TEntity : class, IIdentifiable + { + internal AffectedResources(IEnumerable entities, + Dictionary relationships) + : base(entities, relationships) { } + + public IEnumerator GetEnumerator() + { + return Entities.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public abstract class AffectedResourcesBase : AffectedRelationships, IAffectedResourcesBase where TEntity : class, IIdentifiable + { + public HashSet Entities { get; } + + internal protected AffectedResourcesBase(IEnumerable entities, + Dictionary relationships) : base(relationships) + { + Entities = new HashSet(entities.Cast()); + } + } + +} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs index d48f3dbcee..9f255cda71 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs @@ -1,14 +1,11 @@ - -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Hooks { - /// /// A helper class that provides insight in what is to be updated. The /// property reflects what was parsed from the incoming request, @@ -17,29 +14,55 @@ namespace JsonApiDotNetCore.Hooks /// Any relationships that are updated can be retrieved via the methods implemented on /// . /// - public interface IEntityDiff : IAffectedRelationships where TEntity : class, IIdentifiable + public interface IEntityDiff : IEnumerable>, IAffectedResourcesBase where TEntity : class, IIdentifiable { - HashSet RequestEntities { get; } HashSet DatabaseEntities { get; } } - public class EntityDiff : AffectedRelationships, IEntityDiff where TEntity : class, IIdentifiable + public class EntityDiff : AffectedResourcesBase, IEntityDiff where TEntity : class, IIdentifiable { private readonly HashSet _databaseEntities; + private readonly bool _databaseValuesLoaded; public HashSet DatabaseEntities { get => _databaseEntities ?? ThrowNoDbValuesError(); } - public HashSet RequestEntities { get; private set; } - public EntityDiff(IEnumerable requestEntities, + internal EntityDiff(IEnumerable requestEntities, IEnumerable databaseEntities, - Dictionary relationships) : base(relationships) + Dictionary relationships) : base(requestEntities, relationships) { - RequestEntities = (HashSet)requestEntities; _databaseEntities = (HashSet)databaseEntities; + _databaseValuesLoaded |= _databaseEntities != null; } private HashSet ThrowNoDbValuesError() { throw new MemberAccessException("Cannot access database entities if the LoadDatabaseValues option is set to false"); } + + public IEnumerator> GetEnumerator() + { + foreach (var entity in Entities) + { + TEntity currentValueInDatabase = null; + if (_databaseValuesLoaded) currentValueInDatabase = _databaseEntities.Single(e => entity.StringId == e.StringId); + yield return new EntityDiffPair(entity, currentValueInDatabase); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public class EntityDiffPair where TEntity : class, IIdentifiable + { + internal EntityDiffPair(TEntity entity, TEntity databaseValue) + { + Entity = entity; + DatabaseValue = databaseValue; + } + + public TEntity Entity { get; private set; } + public TEntity DatabaseValue { get; private set; } } } diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs index 3cefb039e3..a1f8b8b4a7 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs @@ -96,7 +96,7 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// The transformed entity set /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. - IEnumerable BeforeCreate(HashSet entities, ResourcePipeline pipeline); + IEnumerable BeforeCreate(IAffectedResources entities, ResourcePipeline pipeline); /// /// Implement this hook to run custom logic in the /// layer just before reading entities of type . @@ -133,7 +133,7 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// The transformed entity set /// The entity diff. /// An enum indicating from where the hook was triggered. - IEnumerable BeforeUpdate(EntityDiff entityDiff, ResourcePipeline pipeline); + IEnumerable BeforeUpdate(IEntityDiff entityDiff, ResourcePipeline pipeline); /// /// Implement this hook to run custom logic in the /// layer just before deleting entities of type . @@ -155,7 +155,7 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// The transformed entity set /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. - IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline); + IEnumerable BeforeDelete(IAffectedResources entities, ResourcePipeline pipeline); /// /// Implement this hook to run custom logic in the /// layer just before updating relationships to entities of type . diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index 50d3f20caf..988c50cba2 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -64,7 +64,8 @@ public virtual IEnumerable BeforeCreate(IEnumerable e { if (GetHook(ResourceHook.BeforeCreate, entities, out var container, out var node)) { - IEnumerable updated = container.BeforeCreate((HashSet)node.UniqueEntities, pipeline); + var affected = new AffectedResources((HashSet)node.UniqueEntities, node.PrincipalsToNextLayer()); + IEnumerable updated = container.BeforeCreate(affected, pipeline); node.UpdateUnique(updated); node.Reassign(entities); } @@ -78,8 +79,10 @@ public virtual IEnumerable BeforeDelete(IEnumerable e { if (GetHook(ResourceHook.BeforeDelete, entities, out var container, out var node)) { - var targetEntities = (LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, node.RelationshipsToNextLayer) ?? node.UniqueEntities); - IEnumerable updated = container.BeforeDelete((HashSet)targetEntities, pipeline); + var targetEntities = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, node.RelationshipsToNextLayer) ?? node.UniqueEntities; + var affected = new AffectedResources(targetEntities, node.PrincipalsToNextLayer()); + + IEnumerable updated = container.BeforeDelete(affected, pipeline); node.UpdateUnique(updated); node.Reassign(entities); } @@ -350,7 +353,7 @@ object ThrowJsonApiExceptionOnError(Func action) IAffectedRelationships CreateRelationshipHelper(DependentType entityType, Dictionary prevLayerRelationships, IEnumerable dbValues = null) { if (dbValues != null) ReplaceWithDbValues(prevLayerRelationships, dbValues.Cast()); - return (IAffectedRelationships)TypeHelper.CreateInstanceOfOpenType(typeof(AffectedRelationships<>), entityType, prevLayerRelationships); + return (IAffectedRelationships)TypeHelper.CreateInstanceOfOpenType(typeof(AffectedRelationships<>), entityType, true, prevLayerRelationships); } /// diff --git a/src/JsonApiDotNetCore/Internal/TypeHelper.cs b/src/JsonApiDotNetCore/Internal/TypeHelper.cs index e7b5f38d19..da3feaad6f 100644 --- a/src/JsonApiDotNetCore/Internal/TypeHelper.cs +++ b/src/JsonApiDotNetCore/Internal/TypeHelper.cs @@ -104,6 +104,18 @@ public static object CreateInstanceOfOpenType(Type openType, Type[] parameters, return Activator.CreateInstance(parameterizedType, constructorArguments); } + /// + /// Use this overload if you need to instantiate a type that has a internal constructor + /// + public static object CreateInstanceOfOpenType(Type openType, Type[] parameters, bool hasInternalConstructor, params object[] constructorArguments) + { + if (!hasInternalConstructor) return CreateInstanceOfOpenType(openType, parameters, constructorArguments); + var parameterizedType = openType.MakeGenericType(parameters); + // note that if for whatever reason the constructor of AffectedResource is set from + // internal to public, this will throw an error, as it is looking for a no + return Activator.CreateInstance(parameterizedType, BindingFlags.NonPublic | BindingFlags.Instance, null, constructorArguments, null); + } + /// /// Creates an instance of the specified generic type /// @@ -116,6 +128,15 @@ public static object CreateInstanceOfOpenType(Type openType, Type parameter, par return CreateInstanceOfOpenType(openType, new Type[] { parameter }, constructorArguments); } + /// + /// Use this overload if you need to instantiate a type that has a internal constructor + /// + public static object CreateInstanceOfOpenType(Type openType, Type parameter, bool hasInternalConstructor, params object[] constructorArguments) + { + return CreateInstanceOfOpenType(openType, new Type[] { parameter }, hasInternalConstructor, constructorArguments); + + } + /// /// Reflectively instantiates a list of a certain type. /// diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 25b957e983..e0213d6775 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -175,13 +175,13 @@ public virtual void AfterDelete(HashSet entities, ResourcePipeline pipeline, /// public virtual void AfterUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { } /// - public virtual IEnumerable BeforeCreate(HashSet entities, ResourcePipeline pipeline) { return entities; } + public virtual IEnumerable BeforeCreate(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } /// public virtual void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { } /// - public virtual IEnumerable BeforeUpdate(EntityDiff entityDiff, ResourcePipeline pipeline) { return entityDiff.RequestEntities; } + public virtual IEnumerable BeforeUpdate(IEntityDiff entityDiff, ResourcePipeline pipeline) { return entityDiff.Entities; } /// - public virtual IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + public virtual IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } /// public virtual IEnumerable BeforeUpdateRelationship(HashSet ids, IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { return ids; } /// diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs index 3edb8fa901..969bef37d2 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs @@ -147,6 +147,13 @@ public async Task Can_Patch_Entity() [Fact] public async Task Patch_Entity_With_HasMany_Does_Not_Included_Relationships() { + /// @TODO: if we add a BeforeUpate resource hook to PersonDefinition + /// with database values enabled, this test will fail because todo-items + /// will be included in the person instance in the database-value loading. + /// This is then attached in the EF dbcontext, so when the query is executed and returned, + /// that entity will still have the relationship included even though the repo didn't include it. + + // arrange var todoItem = _todoItemFaker.Generate(); var person = _personFaker.Generate(); diff --git a/test/UnitTests/ResourceHooks/DiscoveryTests.cs b/test/UnitTests/ResourceHooks/DiscoveryTests.cs index 9fd3a6305b..6af4f6a392 100644 --- a/test/UnitTests/ResourceHooks/DiscoveryTests.cs +++ b/test/UnitTests/ResourceHooks/DiscoveryTests.cs @@ -14,7 +14,7 @@ public class DummyResourceDefinition : ResourceDefinition { public DummyResourceDefinition() : base(new ResourceGraphBuilder().AddResource().Build()) { } - public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + public override IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } } @@ -35,7 +35,7 @@ public abstract class ResourceDefintionBase : ResourceDefinition where T : { protected ResourceDefintionBase(IResourceGraph graph) : base(graph) { } - public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + public override IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } } @@ -59,7 +59,7 @@ public class YetAnotherDummyResourceDefinition : ResourceDefinition().Build()) { } - public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + public override IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } [LoadDatabaseValues(false)] public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } @@ -81,7 +81,7 @@ public class DoubleDummyResourceDefinition1 : ResourceDefinition { public DoubleDummyResourceDefinition1() : base(new ResourceGraphBuilder().AddResource().Build()) { } - public override IEnumerable BeforeDelete(HashSet entities, ResourcePipeline pipeline) { return entities; } + public override IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected.Entities; } } public class DoubleDummyResourceDefinition2 : ResourceDefinition { diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs index e196c04f76..4a35411171 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs @@ -24,7 +24,7 @@ public void BeforeCreate() // act hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -43,7 +43,7 @@ public void BeforeCreate_Without_Parent_Hook_Implemented() // act hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Never()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Never()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -61,7 +61,7 @@ public void BeforeCreate_Without_Child_Hook_Implemented() // act hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } [Fact] diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs index 978434823e..11d1af329a 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs @@ -54,7 +54,7 @@ public void BeforeCreate() hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.IsAny>(), @@ -101,7 +101,7 @@ public void BeforeCreate_Without_Child_Hook_Implemented() hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( It.Is>(rh => TodoCheck(rh, description + description)), ResourcePipeline.Post), @@ -122,7 +122,7 @@ public void BeforeCreate_NoImplicit() hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.IsAny>(), @@ -165,7 +165,7 @@ public void BeforeCreate_NoImplicit_Without_Child_Hook_Implemented() hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDeleteTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDeleteTests.cs index 0a2d3c70be..763d0716c4 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDeleteTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDeleteTests.cs @@ -22,7 +22,7 @@ public void BeforeDelete() hookExecutor.BeforeDelete(todoList, ResourcePipeline.Delete); // assert - resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); resourceDefinitionMock.VerifyNoOtherCalls(); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDelete_WithDbValue_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDelete_WithDbValue_Tests.cs index edb4be9a96..7c23e8d133 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDelete_WithDbValue_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDelete_WithDbValue_Tests.cs @@ -47,7 +47,7 @@ public void BeforeDelete() hookExecutor.BeforeDelete(new List { person }, ResourcePipeline.Delete); // assert - personResourceMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); + personResourceMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>( rh => CheckImplicitTodos(rh) ), ResourcePipeline.Delete), Times.Once()); passportResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>( rh => CheckImplicitPassports(rh) ), ResourcePipeline.Delete), Times.Once()); VerifyNoOtherCalls(personResourceMock, todoResourceMock, passportResourceMock); @@ -88,7 +88,7 @@ public void BeforeDelete_No_Children_Hooks() hookExecutor.BeforeDelete(new List { person }, ResourcePipeline.Delete); // assert - personResourceMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); + personResourceMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); VerifyNoOtherCalls(personResourceMock, todoResourceMock, passportResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs index d354f7ad62..d6926fec34 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs @@ -24,7 +24,7 @@ public void BeforeUpdate() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -62,7 +62,7 @@ public void BeforeUpdate_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs index 739530e7c3..70d1227790 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs @@ -59,7 +59,7 @@ public void BeforeUpdate() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.Is>(rh => PersonCheck(lastName, rh)), @@ -93,7 +93,7 @@ public void BeforeUpdate_Deleting_Relationship() hookExecutor.BeforeUpdate(_todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( It.Is>(rh => PersonCheck(lastName + lastName, rh)), ResourcePipeline.Patch), @@ -140,7 +140,7 @@ public void BeforeUpdate_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( It.Is>(rh => TodoCheck(rh, description + description)), ResourcePipeline.Patch), @@ -161,7 +161,7 @@ public void BeforeUpdate_NoImplicit() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.IsAny>(), @@ -204,14 +204,14 @@ public void BeforeUpdate_NoImplicit_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } - private bool TodoCheck(EntityDiff diff, string checksum) + private bool TodoCheck(IEntityDiff diff, string checksum) { var dbCheck = diff.DatabaseEntities.Single().Description == checksum; - var reqCheck = diff.RequestEntities.Single().Description == null; + var reqCheck = diff.Single().Entity.Description == null; return (dbCheck && reqCheck); } diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index c26968511f..4e16179ab3 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -254,18 +254,18 @@ protected DbContextOptions InitInMemoryDb(Action seeder void MockHooks(Mock> resourceDefinition) where TModel : class, IIdentifiable { resourceDefinition - .Setup(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny())) + .Setup(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny())) .Returns, ResourcePipeline>((entities, context) => entities) .Verifiable(); resourceDefinition .Setup(rd => rd.BeforeRead(It.IsAny(), It.IsAny(), It.IsAny())) .Verifiable(); resourceDefinition - .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) - .Returns, ResourcePipeline>((entityDiff, context) => entityDiff.RequestEntities) + .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) + .Returns, ResourcePipeline>((entityDiff, context) => entityDiff.Entities) .Verifiable(); resourceDefinition - .Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())) + .Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())) .Returns, ResourcePipeline>((entities, context) => entities) .Verifiable(); resourceDefinition From 6a7c8d1d2ed3a53bdce216490574526da08707ea Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 6 Jun 2019 12:21:33 +0200 Subject: [PATCH 162/168] documentation: upgrade --- docs/usage/resources/hooks.md | 92 +++++++++++++++---- .../Hooks/Execution/EntityDiff.cs | 6 +- .../Update/BeforeUpdate_WithDbValues_Tests.cs | 2 +- 3 files changed, 79 insertions(+), 21 deletions(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index 6922880cb2..ba179321af 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -1,4 +1,5 @@ + # Resource Hooks This section covers the usage of **Resource Hooks**, which is a feature of`ResourceDefinition`. See the [ResourceDefinition usage guide](resource-definitions.md) for a general explanation on how to set up a `ResourceDefinition`. For a quick start, jump right to the [Getting started: most minimal example](#getting-started-most-minimal-example) section. @@ -12,16 +13,18 @@ This usage guide covers the following sections 1. [**Semantics: pipelines, actions and hooks**](#semantics-pipelines-actions-and-hooks) Understanding the semantics will be helpful in identifying which hooks on `ResourceDefinition` you need to implement for your use-case. 2. [**Basic usage**](#basic-usage) + Some examples to get you started. * [**Getting started: most minimal example**](#getting-started-most-minimal-example) * [**Logging**](#logging) * [**Transforming data with OnReturn**](#transforming-data-with-onreturn) * [**Loading database values**](#loading-database-values) 3. [**Advanced usage**](#advanced-usage) + Complicated examples that show the advanced features of hooks. * [**Simple authorization: explicitly affected resources**](#simple-authorization-explicitly-affected-resources) * [**Advanced authorization: implicitly affected resources**](#advanced-authorization-implicitly-affected-resources) * [**Synchronizing data across microservices**](#synchronizing-data-across-microservices) * [**Hooks for many-to-many join tables**](#hooks-for-many-to-many-join-tables) -4. [**Hook execution overview**](#hook-execution-overview) +5. [**Hook execution overview**](#hook-execution-overview) A table overview of all pipelines and involved hooks # 1. Semantics: pipelines, actions and hooks @@ -176,7 +179,7 @@ public class PersonResource : ResourceDefinition _userLogger = logService.Instance; } - public override void AfterUpdateRelationship(IUpdatedRelationshipHelper relationshipHelper, ResourcePipeline pipeline) + public override void AfterUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { var updatedRelationshipsToArticle = relationshipHelper.EntitiesRelatedTo
(); foreach (var updated in updatedRelationshipsToArticle) @@ -257,30 +260,35 @@ public class ArticleResource : ResourceDefinition
_context = context; } - public override IEnumerable
BeforeUpdate(EntityDiff
entityDiff, ResourcePipeline pipeline) + public override IEnumerable
BeforeUpdate(IEntityDiff
entityDiff, ResourcePipeline pipeline) { // PropertyGetter is a helper class that takes care of accessing the values on an instance of Article using reflection. var getter = new PropertyGetter
(); - - entityDiff.RequestEntities.ForEach( requestEntity => + + // EntityDiff is a class that is like a list that contains EntityDiffPair elements + foreach (EntityDiffPair
affected in entityDiff) { + var currentDatabaseState = affected.DatabaseValue; // the current state in the database + var proposedValueFromRequest = affected.Entity; // the value from the request - var databaseEntity = entityDiff.DatabaseEntities.Single( e => e.Id == a.Id); + if (currentDatabaseState.IsLocked) throw new JsonApiException(403, "Forbidden: this article is locked!") - if (databaseEntity.IsLocked) throw new JsonApiException(403, "Forbidden: this article is locked!") - - foreach (var attr in _context.AttributesToUpdate) + foreach (var attr in _context.AttributesToUpdate) { - var oldValue = getter(databaseEntity, attr); - var newValue = getter(requestEntity, attr); + var oldValue = getter(currentDatabaseState, attr); + var newValue = getter(proposedValueFromRequest, attr); _logger.LogAttributeUpdate(oldValue, newValue) } - }); - return entityDiff.RequestEntities; + } + // You must return IEnumerable
from this hook. + // This means that you could reduce the set of entities that is + // affected by this request, eg. by entityDiff.Entities.Where( ... ); + entityDiff.Entities; } } ``` +In this case the `EntityDiffPair.DatabaseValue` is `null`. If you try to access all database values at once (`EntityDiff.DatabaseValues`) when it they are turned off, an exception will be thrown. Note that database values are turned on by default. They can be turned of globally by configuring the startup as follows: ```c# @@ -297,15 +305,15 @@ public void ConfigureServices(IServiceCollection services) } ``` -The global setting can be used together with toggling the option on and off on the level of individual hooks using the `LoadDatabaseValues` attribute: +The global setting can be used together with per-hook configuration hooks using the `LoadDatabaseValues` attribute: ```c# public class ArticleResource : ResourceDefinition
{ [LoadDatabaseValues(true)] - public override IEnumerable
BeforeUpdate(EntityDiff
entityDiff, ResourcePipeline pipeline) + public override IEnumerable
BeforeUpdate(IEntityDiff
entityDiff, ResourcePipeline pipeline) { .... - } + } [LoadDatabaseValues(false)] public override IEnumerable BeforeUpdateRelationships(HashSet ids, IAffectedRelationships
resourcesByRelationship, ResourcePipeline pipeline) @@ -314,6 +322,7 @@ public class ArticleResource : ResourceDefinition
// are plain resource identifier objects when LoadDatabaseValues is turned off, // or objects loaded from the database when LoadDatabaseValues is turned on. .... + } } } ``` @@ -321,6 +330,7 @@ public class ArticleResource : ResourceDefinition
Note that there are some hooks that the `LoadDatabaseValues` option and attribute does not affect. The only hooks that are affected are: * `BeforeUpdate` * `BeforeUpdateRelationship` +* `BeforeDelete` @@ -379,7 +389,7 @@ This authorization requirement can be fulfilled as follows. For checking the permissions for the explicitly affected resources, `New Article` and `Alice`, we may implement the `BeforeUpdate` hook for `Article`: ```c# -public override IEnumerable
BeforeUpdate(EntityDiff
entityDiff, ResourcePipeline pipeline) +public override IEnumerable
BeforeUpdate(IEntityDiff
entityDiff, ResourcePipeline pipeline) { if (pipeline == ResourcePipeline.Patch) { @@ -444,6 +454,54 @@ public override void BeforeImplicitUpdateRelationship(IAffectedRelationships` +2. If you are using custom services, you will be responsible for injecting the `IResourceHookExecutor` service into your services and call the appropriate methods. See the [hook execution overview](#hook-execution-overview) to determine which hook should be fired in which scenario. + +If you are required to use the `BeforeImplicitUpdateRelationship` hook (see previous example), there is an additional requirement. For this hook, given a particular relationship, JsonApiDotNetCore needs to be able to resolve the inverse relationship. For example: if `Article` has one author (a `Person`), then it needs to be able to resolve the `RelationshipAttribute` that corresponds to the inverse relationship for the `author` property. There are two approaches : + +1. **Tell JsonApiDotNetCore how to do this only for the relevant models**. If you're using the `BeforeImplicitUpdateRelationship` hook only for a small set of models, eg only for the relationship of the example, then it is easiest to provide the `inverseNavigationProperty` as follows: +```c# +public class Article : Identifiable +{ + ... + [HasOne("author", inverseNavigationProperty: "OwnerOfArticle")] + public virtual Person Author { get; set; } + ... +} +public class Person : Identifiable +{ + ... + [HasOne("article")] + public virtual Article OwnerOfArticle { get; set; } + ... +} +``` +2. **Tell JsonApiDotNetCore how to do this in general**. For full support, you can provide JsonApiDotNetCore with a custom service implementation of the `IInverseRelationships` interface. relationship of the example, then it is easiest to provide the `inverseNavigationProperty` as follows: +```c# +public class CustomInverseRelationshipsResolver : IInverseRelationships +{ + public void Resolve() + { + // the implementation of this method depends completely + // the data access layer you're using. + // It should set the RelationshipAttribute.InverseRelationship property + // for all (relevant) relationships. + // To have an idea of how to implement this method, see the InverseRelationships class + // in the source code of JADNC: + // https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/59a93590ac4f05c9c246eca9459b49e331250805/src/JsonApiDotNetCore/Internal/InverseRelationships.cs + } +} +``` +This service will then be called run once at startup and take care of the metadata that is required for `BeforeImplicitUpdateRelationship` to be supported. + +*Note: don't forget to register this singleton service with the service provider.* + + + ## Synchronizing data across microservices If your application is built using a microservices infrastructure, it may be relevant to propagate data changes between microservices, [see this article for more information](https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/multi-container-microservice-net-applications/integration-event-based-microservice-communications). In this example, we will assume the implementation of an event bus and we will publish data consistency integration events using resource hooks. diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs index 9f255cda71..d2a56f1c1c 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs @@ -9,21 +9,21 @@ namespace JsonApiDotNetCore.Hooks /// /// A helper class that provides insight in what is to be updated. The /// property reflects what was parsed from the incoming request, - /// where the reflects what is the current state in the database. + /// where the reflects what is the current state in the database. /// /// Any relationships that are updated can be retrieved via the methods implemented on /// . /// public interface IEntityDiff : IEnumerable>, IAffectedResourcesBase where TEntity : class, IIdentifiable { - HashSet DatabaseEntities { get; } + HashSet DatabaseValues { get; } } public class EntityDiff : AffectedResourcesBase, IEntityDiff where TEntity : class, IIdentifiable { private readonly HashSet _databaseEntities; private readonly bool _databaseValuesLoaded; - public HashSet DatabaseEntities { get => _databaseEntities ?? ThrowNoDbValuesError(); } + public HashSet DatabaseValues { get => _databaseEntities ?? ThrowNoDbValuesError(); } internal EntityDiff(IEnumerable requestEntities, IEnumerable databaseEntities, diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs index 70d1227790..8d892cd064 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs @@ -210,7 +210,7 @@ public void BeforeUpdate_NoImplicit_Without_Child_Hook_Implemented() private bool TodoCheck(IEntityDiff diff, string checksum) { - var dbCheck = diff.DatabaseEntities.Single().Description == checksum; + var dbCheck = diff.DatabaseValues.Single().Description == checksum; var reqCheck = diff.Single().Entity.Description == null; return (dbCheck && reqCheck); } From d6d4a6147de8d915b2491e637d8396639ab933db Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 6 Jun 2019 15:11:57 +0200 Subject: [PATCH 163/168] refactor: rename entitydiff to resourcediff --- docs/usage/resources/hooks.md | 12 ++++++------ .../Resources/PersonResource.cs | 2 +- .../Hooks/Execution/AffectedResources.cs | 10 +++++----- .../Hooks/Execution/EntityDiff.cs | 18 +++++++++--------- .../Hooks/IResourceHookContainer.cs | 8 ++++---- .../Hooks/ResourceHookExecutor.cs | 2 +- .../Models/ResourceDefinition.cs | 2 +- .../Update/BeforeUpdateTests.cs | 4 ++-- .../Update/BeforeUpdate_WithDbValues_Tests.cs | 12 ++++++------ .../ResourceHooks/ResourceHooksTestsSetup.cs | 4 ++-- 10 files changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/usage/resources/hooks.md b/docs/usage/resources/hooks.md index ba179321af..8e3f0e9ae8 100644 --- a/docs/usage/resources/hooks.md +++ b/docs/usage/resources/hooks.md @@ -260,13 +260,13 @@ public class ArticleResource : ResourceDefinition
_context = context; } - public override IEnumerable
BeforeUpdate(IEntityDiff
entityDiff, ResourcePipeline pipeline) + public override IEnumerable
BeforeUpdate(IResourceDiff
entityDiff, ResourcePipeline pipeline) { // PropertyGetter is a helper class that takes care of accessing the values on an instance of Article using reflection. var getter = new PropertyGetter
(); - // EntityDiff is a class that is like a list that contains EntityDiffPair elements - foreach (EntityDiffPair
affected in entityDiff) + // ResourceDiff is a class that is like a list that contains ResourceDiffPair elements + foreach (ResourceDiffPair
affected in entityDiff) { var currentDatabaseState = affected.DatabaseValue; // the current state in the database var proposedValueFromRequest = affected.Entity; // the value from the request @@ -288,7 +288,7 @@ public class ArticleResource : ResourceDefinition
} } ``` -In this case the `EntityDiffPair.DatabaseValue` is `null`. If you try to access all database values at once (`EntityDiff.DatabaseValues`) when it they are turned off, an exception will be thrown. +In this case the `ResourceDiffPair.DatabaseValue` is `null`. If you try to access all database values at once (`ResourceDiff.DatabaseValues`) when it they are turned off, an exception will be thrown. Note that database values are turned on by default. They can be turned of globally by configuring the startup as follows: ```c# @@ -310,7 +310,7 @@ The global setting can be used together with per-hook configuration hooks using public class ArticleResource : ResourceDefinition
{ [LoadDatabaseValues(true)] - public override IEnumerable
BeforeUpdate(IEntityDiff
entityDiff, ResourcePipeline pipeline) + public override IEnumerable
BeforeUpdate(IResourceDiff
entityDiff, ResourcePipeline pipeline) { .... } @@ -389,7 +389,7 @@ This authorization requirement can be fulfilled as follows. For checking the permissions for the explicitly affected resources, `New Article` and `Alice`, we may implement the `BeforeUpdate` hook for `Article`: ```c# -public override IEnumerable
BeforeUpdate(IEntityDiff
entityDiff, ResourcePipeline pipeline) +public override IEnumerable
BeforeUpdate(IResourceDiff
entityDiff, ResourcePipeline pipeline) { if (pipeline == ResourcePipeline.Patch) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs index ec6b4458ab..50e9efb54c 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs @@ -17,7 +17,7 @@ public override IEnumerable BeforeUpdateRelationship(HashSet ids } //[LoadDatabaseValues(true)] - //public override IEnumerable BeforeUpdate(IEntityDiff entityDiff, ResourcePipeline pipeline) + //public override IEnumerable BeforeUpdate(IResourceDiff entityDiff, ResourcePipeline pipeline) //{ // return entityDiff.Entities; //} diff --git a/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs index 63c1d503c1..2c0a8daa7f 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs @@ -14,12 +14,12 @@ public interface IAffectedResources : IAffectedResourcesBase, /// NOTE: you might wonder why there is a separate AffectedResourceBase and AffectedResource. /// If we merge them together, ie get rid of the base and just let the AffectedResource directly implement IEnumerable{TEntity}, /// we will run in to problems with the following: - /// EntityDiff{} inherits from AffectedResource{TEntity}, - /// but EntityDiff also implements IEnumerable{EntityDiffPair{TEntity}}. This means that - /// EntityDiff will implement two IEnumerable{x} where (x1 = TEntity and x2 = EntityDiffPair{TEntity} ) + /// ResourceDiff{} inherits from AffectedResource{TEntity}, + /// but ResourceDiff also implements IEnumerable{ResourceDiffPair{TEntity}}. This means that + /// ResourceDiff will implement two IEnumerable{x} where (x1 = TEntity and x2 = ResourceDiffPair{TEntity} ) /// The problem with this is that when you then try to do a simple foreach loop over - /// a EntityDiff instance, it will throw an error, because it does not know which of the two enumerators to pick. - /// We want EntityDiff to only loop over the EntityDiffPair, so we can do that by making sure + /// a ResourceDiff instance, it will throw an error, because it does not know which of the two enumerators to pick. + /// We want ResourceDiff to only loop over the ResourceDiffPair, so we can do that by making sure /// it doesn't inherit the IEnumerable{TEntity} part from AffectedResources. public interface IAffectedResourcesBase where TEntity : class, IIdentifiable { diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs index d2a56f1c1c..c8e01c769d 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs @@ -8,24 +8,24 @@ namespace JsonApiDotNetCore.Hooks { /// /// A helper class that provides insight in what is to be updated. The - /// property reflects what was parsed from the incoming request, - /// where the reflects what is the current state in the database. + /// property reflects what was parsed from the incoming request, + /// where the reflects what is the current state in the database. /// /// Any relationships that are updated can be retrieved via the methods implemented on /// . /// - public interface IEntityDiff : IEnumerable>, IAffectedResourcesBase where TEntity : class, IIdentifiable + public interface IResourceDiff : IEnumerable>, IAffectedResourcesBase where TEntity : class, IIdentifiable { HashSet DatabaseValues { get; } } - public class EntityDiff : AffectedResourcesBase, IEntityDiff where TEntity : class, IIdentifiable + public class ResourceDiff : AffectedResourcesBase, IResourceDiff where TEntity : class, IIdentifiable { private readonly HashSet _databaseEntities; private readonly bool _databaseValuesLoaded; public HashSet DatabaseValues { get => _databaseEntities ?? ThrowNoDbValuesError(); } - internal EntityDiff(IEnumerable requestEntities, + internal ResourceDiff(IEnumerable requestEntities, IEnumerable databaseEntities, Dictionary relationships) : base(requestEntities, relationships) { @@ -38,13 +38,13 @@ private HashSet ThrowNoDbValuesError() throw new MemberAccessException("Cannot access database entities if the LoadDatabaseValues option is set to false"); } - public IEnumerator> GetEnumerator() + public IEnumerator> GetEnumerator() { foreach (var entity in Entities) { TEntity currentValueInDatabase = null; if (_databaseValuesLoaded) currentValueInDatabase = _databaseEntities.Single(e => entity.StringId == e.StringId); - yield return new EntityDiffPair(entity, currentValueInDatabase); + yield return new ResourceDiffPair(entity, currentValueInDatabase); } } @@ -54,9 +54,9 @@ IEnumerator IEnumerable.GetEnumerator() } } - public class EntityDiffPair where TEntity : class, IIdentifiable + public class ResourceDiffPair where TEntity : class, IIdentifiable { - internal EntityDiffPair(TEntity entity, TEntity databaseValue) + internal ResourceDiffPair(TEntity entity, TEntity databaseValue) { Entity = entity; DatabaseValue = databaseValue; diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs index a1f8b8b4a7..6661c52814 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs @@ -110,12 +110,12 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// layer just before updating entities of type . /// /// For the pipeline, the - /// will typically contain one entity. + /// will typically contain one entity. /// For , this it may contain /// multiple entities. /// /// The returned may be a subset - /// of the property in parameter , + /// 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. @@ -131,9 +131,9 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// hook is fired for these. ///
/// The transformed entity set - /// The entity diff. + /// The entity diff. /// An enum indicating from where the hook was triggered. - IEnumerable BeforeUpdate(IEntityDiff entityDiff, ResourcePipeline pipeline); + IEnumerable BeforeUpdate(IResourceDiff ResourceDiff, ResourcePipeline pipeline); /// /// Implement this hook to run custom logic in the /// layer just before deleting entities of type . diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index 988c50cba2..aca190225a 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -48,7 +48,7 @@ public virtual IEnumerable BeforeUpdate(IEnumerable e if (GetHook(ResourceHook.BeforeUpdate, entities, out var container, out var node)) { var dbValues = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, node.RelationshipsToNextLayer); - var diff = new EntityDiff(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer()); + var diff = new ResourceDiff(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer()); IEnumerable updated = container.BeforeUpdate(diff, pipeline); node.UpdateUnique(updated); node.Reassign(entities); diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index e0213d6775..1124364f9c 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -179,7 +179,7 @@ public virtual void AfterUpdateRelationship(IAffectedRelationships resourcesB /// public virtual void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { } /// - public virtual IEnumerable BeforeUpdate(IEntityDiff entityDiff, ResourcePipeline pipeline) { return entityDiff.Entities; } + public virtual IEnumerable BeforeUpdate(IResourceDiff ResourceDiff, ResourcePipeline pipeline) { return ResourceDiff.Entities; } /// public virtual IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } /// diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs index d6926fec34..1565418778 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs @@ -24,7 +24,7 @@ public void BeforeUpdate() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -62,7 +62,7 @@ public void BeforeUpdate_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs index 8d892cd064..99b343de8c 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs @@ -59,7 +59,7 @@ public void BeforeUpdate() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.Is>(rh => PersonCheck(lastName, rh)), @@ -93,7 +93,7 @@ public void BeforeUpdate_Deleting_Relationship() hookExecutor.BeforeUpdate(_todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( It.Is>(rh => PersonCheck(lastName + lastName, rh)), ResourcePipeline.Patch), @@ -140,7 +140,7 @@ public void BeforeUpdate_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( It.Is>(rh => TodoCheck(rh, description + description)), ResourcePipeline.Patch), @@ -161,7 +161,7 @@ public void BeforeUpdate_NoImplicit() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.IsAny>(), @@ -204,11 +204,11 @@ public void BeforeUpdate_NoImplicit_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } - private bool TodoCheck(IEntityDiff diff, string checksum) + private bool TodoCheck(IResourceDiff diff, string checksum) { var dbCheck = diff.DatabaseValues.Single().Description == checksum; var reqCheck = diff.Single().Entity.Description == null; diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 4e16179ab3..545c009ae0 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -261,8 +261,8 @@ void MockHooks(Mock> resourceDefinition) .Setup(rd => rd.BeforeRead(It.IsAny(), It.IsAny(), It.IsAny())) .Verifiable(); resourceDefinition - .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) - .Returns, ResourcePipeline>((entityDiff, context) => entityDiff.Entities) + .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) + .Returns, ResourcePipeline>((entityDiff, context) => entityDiff.Entities) .Verifiable(); resourceDefinition .Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())) From ad85758c3805d0245a82fdb7bd797e81b75af4b6 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 6 Jun 2019 18:57:36 +0200 Subject: [PATCH 164/168] fix: traversal bug related to parsing next layer relationships when layer count > 2 --- .../Extensions/IQueryableExtensions.cs | 8 ++++++++ .../Hooks/Traversal/TraversalHelper.cs | 10 ++++++++-- src/JsonApiDotNetCore/Models/RelationshipAttribute.cs | 4 +++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs index 81f15c2173..8407ede0d3 100644 --- a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs @@ -430,5 +430,13 @@ public static IQueryable PageForward(this IQueryable source, int pageSi return source; } + public static void ForEach(this IEnumerable enumeration, Action action) + { + foreach (T item in enumeration) + { + action(item); + } + } + } } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs index 6e4ccab1b3..50b155daff 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs @@ -55,6 +55,7 @@ public TraversalHelper( public RootNode CreateRootNode(IEnumerable rootEntities) where TEntity : class, IIdentifiable { _processedEntities = new Dictionary>(); + RegisterRelationshipProxies(typeof(TEntity)); var uniqueEntities = ProcessEntities(rootEntities); var relationshipsToNextLayer = GetRelationships(typeof(TEntity)); return new RootNode(uniqueEntities, relationshipsToNextLayer); @@ -94,7 +95,7 @@ public EntityChildLayer CreateNextLayer(IEnumerable nodes) return CreateRelationsipGroupInstance(nextNodeType, proxy, grouped.Value, principals[proxy]); }).ToList(); - + RegisterRelationshipProxies(nextNodeType); return CreateNodeInstance(nextNodeType, GetRelationships(nextNodeType), relationshipsToPreviousLayer); }).ToList(); @@ -118,6 +119,8 @@ Dictionary>(); var dependentsGrouped = new Dictionary>(); + principalNodes.ForEach(n => RegisterRelationshipProxies(n.EntityType)); + foreach (var node in principalNodes) { var principalEntities = node.UniqueEntities; @@ -181,7 +184,11 @@ HashSet ProcessEntities(IEnumerable incomingEntities) Type type = typeof(TEntity); var newEntities = UniqueInTree(incomingEntities, type); RegisterProcessedEntities(newEntities, type); + return newEntities; + } + void RegisterRelationshipProxies(DependentType type) + { var contextEntity = _graph.GetContextEntity(type); foreach (RelationshipAttribute attr in contextEntity.Relationships) { @@ -194,7 +201,6 @@ HashSet ProcessEntities(IEnumerable incomingEntities) RelationshipProxies[attr] = proxy; } } - return newEntities; } diff --git a/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs index 0ec1ca2c03..0e873a16fc 100644 --- a/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs +++ b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs @@ -99,7 +99,9 @@ public override bool Equals(object obj) { return false; } - return IsHasMany == attr.IsHasMany && PublicRelationshipName.Equals(attr.PublicRelationshipName); + bool equalRelationshipName = PublicRelationshipName.Equals(attr.PublicRelationshipName); + bool equalPrincipalType = PrincipalType.Equals(attr.PrincipalType); + return IsHasMany == attr.IsHasMany && equalRelationshipName && equalPrincipalType; } /// From 5cd07d52118f98d48280ca78e2246c8ab0ec4a2a Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 6 Jun 2019 19:06:46 +0200 Subject: [PATCH 165/168] fix: added backward compatability relationship attr equality check --- src/JsonApiDotNetCore/Models/RelationshipAttribute.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs index 0e873a16fc..44123a082e 100644 --- a/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs +++ b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs @@ -100,7 +100,12 @@ public override bool Equals(object obj) return false; } bool equalRelationshipName = PublicRelationshipName.Equals(attr.PublicRelationshipName); - bool equalPrincipalType = PrincipalType.Equals(attr.PrincipalType); + + bool equalPrincipalType = true; + if (PrincipalType != null) + { + equalPrincipalType = PrincipalType.Equals(attr.PrincipalType); + } return IsHasMany == attr.IsHasMany && equalRelationshipName && equalPrincipalType; } From 86dfd8d0656b4e16ca8f9c6ba5118228c741ee7e Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 11 Jun 2019 15:41:08 +0200 Subject: [PATCH 166/168] refactor: AffectedResourcesDiff --- ...{EntityDiff.cs => AffectedResourceDiff.cs} | 35 ++++++++-------- .../Hooks/Execution/AffectedResources.cs | 42 +++++-------------- .../Hooks/IResourceHookContainer.cs | 2 +- .../Models/ResourceDefinition.cs | 2 +- .../Update/BeforeUpdateTests.cs | 4 +- .../Update/BeforeUpdate_WithDbValues_Tests.cs | 17 ++++---- .../ResourceHooks/ResourceHooksTestsSetup.cs | 2 +- 7 files changed, 43 insertions(+), 61 deletions(-) rename src/JsonApiDotNetCore/Hooks/Execution/{EntityDiff.cs => AffectedResourceDiff.cs} (62%) diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResourceDiff.cs similarity index 62% rename from src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs rename to src/JsonApiDotNetCore/Hooks/Execution/AffectedResourceDiff.cs index c8e01c769d..59102a50ad 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiff.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResourceDiff.cs @@ -8,49 +8,50 @@ namespace JsonApiDotNetCore.Hooks { /// /// A helper class that provides insight in what is to be updated. The - /// property reflects what was parsed from the incoming request, - /// where the reflects what is the current state in the database. + /// property reflects what was parsed from the incoming request, + /// where the reflects what is the current state in the database. /// /// Any relationships that are updated can be retrieved via the methods implemented on /// . /// - public interface IResourceDiff : IEnumerable>, IAffectedResourcesBase where TEntity : class, IIdentifiable + public interface IAffectedResourcesDiff : IAffectedResources where TEntity : class, IIdentifiable { HashSet DatabaseValues { get; } + IEnumerable> GetDiff(); } - public class ResourceDiff : AffectedResourcesBase, IResourceDiff where TEntity : class, IIdentifiable + public class ResourceDiff : AffectedResources, IAffectedResourcesDiff where TEntity : class, IIdentifiable { - private readonly HashSet _databaseEntities; + + private readonly HashSet _databaseValues; private readonly bool _databaseValuesLoaded; - public HashSet DatabaseValues { get => _databaseEntities ?? ThrowNoDbValuesError(); } + + /// + /// the current database values of the affected resources collection. + /// + public HashSet DatabaseValues { get => _databaseValues ?? ThrowNoDbValuesError(); } internal ResourceDiff(IEnumerable requestEntities, IEnumerable databaseEntities, Dictionary relationships) : base(requestEntities, relationships) { - _databaseEntities = (HashSet)databaseEntities; - _databaseValuesLoaded |= _databaseEntities != null; + _databaseValues = (HashSet)databaseEntities; + _databaseValuesLoaded |= _databaseValues != null; } - private HashSet ThrowNoDbValuesError() - { - throw new MemberAccessException("Cannot access database entities if the LoadDatabaseValues option is set to false"); - } - - public IEnumerator> GetEnumerator() + public IEnumerable> GetDiff() { foreach (var entity in Entities) { TEntity currentValueInDatabase = null; - if (_databaseValuesLoaded) currentValueInDatabase = _databaseEntities.Single(e => entity.StringId == e.StringId); + if (_databaseValuesLoaded) currentValueInDatabase = _databaseValues.Single(e => entity.StringId == e.StringId); yield return new ResourceDiffPair(entity, currentValueInDatabase); } } - IEnumerator IEnumerable.GetEnumerator() + private HashSet ThrowNoDbValuesError() { - return GetEnumerator(); + throw new MemberAccessException("Cannot access database entities if the LoadDatabaseValues option is set to false"); } } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs index 2c0a8daa7f..a004dcc244 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs @@ -6,32 +6,23 @@ namespace JsonApiDotNetCore.Hooks { - public interface IAffectedResources : IAffectedResourcesBase, IEnumerable where TEntity : class, IIdentifiable - { - - } - - /// NOTE: you might wonder why there is a separate AffectedResourceBase and AffectedResource. - /// If we merge them together, ie get rid of the base and just let the AffectedResource directly implement IEnumerable{TEntity}, - /// we will run in to problems with the following: - /// ResourceDiff{} inherits from AffectedResource{TEntity}, - /// but ResourceDiff also implements IEnumerable{ResourceDiffPair{TEntity}}. This means that - /// ResourceDiff will implement two IEnumerable{x} where (x1 = TEntity and x2 = ResourceDiffPair{TEntity} ) - /// The problem with this is that when you then try to do a simple foreach loop over - /// a ResourceDiff instance, it will throw an error, because it does not know which of the two enumerators to pick. - /// We want ResourceDiff to only loop over the ResourceDiffPair, so we can do that by making sure - /// it doesn't inherit the IEnumerable{TEntity} part from AffectedResources. - public interface IAffectedResourcesBase where TEntity : class, IIdentifiable + public interface IAffectedResources : IEnumerable where TEntity : class, IIdentifiable { HashSet Entities { get; } } - public class AffectedResources : AffectedResourcesBase, IAffectedResources where TEntity : class, IIdentifiable + public class AffectedResources : AffectedRelationships, IAffectedResources where TEntity : class, IIdentifiable { - internal AffectedResources(IEnumerable entities, - Dictionary relationships) - : base(entities, relationships) { } + /// + /// The entities that are affected by the request. + /// + public HashSet Entities { get; } + internal AffectedResources(IEnumerable entities, + Dictionary relationships) : base(relationships) + { + Entities = new HashSet(entities.Cast()); + } public IEnumerator GetEnumerator() { return Entities.GetEnumerator(); @@ -43,15 +34,4 @@ IEnumerator IEnumerable.GetEnumerator() } } - public abstract class AffectedResourcesBase : AffectedRelationships, IAffectedResourcesBase where TEntity : class, IIdentifiable - { - public HashSet Entities { get; } - - internal protected AffectedResourcesBase(IEnumerable entities, - Dictionary relationships) : base(relationships) - { - Entities = new HashSet(entities.Cast()); - } - } - } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs index 6661c52814..7597a02d6e 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs @@ -133,7 +133,7 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// The transformed entity set /// The entity diff. /// An enum indicating from where the hook was triggered. - IEnumerable BeforeUpdate(IResourceDiff ResourceDiff, ResourcePipeline pipeline); + IEnumerable BeforeUpdate(IAffectedResourcesDiff ResourceDiff, ResourcePipeline pipeline); /// /// Implement this hook to run custom logic in the /// layer just before deleting entities of type . diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 1124364f9c..c8fbec627d 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -179,7 +179,7 @@ public virtual void AfterUpdateRelationship(IAffectedRelationships resourcesB /// public virtual void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { } /// - public virtual IEnumerable BeforeUpdate(IResourceDiff ResourceDiff, ResourcePipeline pipeline) { return ResourceDiff.Entities; } + public virtual IEnumerable BeforeUpdate(IAffectedResourcesDiff ResourceDiff, ResourcePipeline pipeline) { return ResourceDiff; } /// public virtual IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } /// diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs index 1565418778..67195f2d95 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs @@ -24,7 +24,7 @@ public void BeforeUpdate() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -62,7 +62,7 @@ public void BeforeUpdate_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs index 99b343de8c..42b7151a07 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs @@ -59,7 +59,7 @@ public void BeforeUpdate() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.Is>(rh => PersonCheck(lastName, rh)), @@ -93,7 +93,7 @@ public void BeforeUpdate_Deleting_Relationship() hookExecutor.BeforeUpdate(_todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( It.Is>(rh => PersonCheck(lastName + lastName, rh)), ResourcePipeline.Patch), @@ -140,7 +140,7 @@ public void BeforeUpdate_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( It.Is>(rh => TodoCheck(rh, description + description)), ResourcePipeline.Patch), @@ -161,7 +161,7 @@ public void BeforeUpdate_NoImplicit() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), It.IsAny>(), @@ -204,14 +204,15 @@ public void BeforeUpdate_NoImplicit_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } - private bool TodoCheck(IResourceDiff diff, string checksum) + private bool TodoCheck(IAffectedResourcesDiff diff, string checksum) { - var dbCheck = diff.DatabaseValues.Single().Description == checksum; - var reqCheck = diff.Single().Entity.Description == null; + var diffPair = diff.GetDiff().Single(); + var dbCheck = diffPair.DatabaseValue.Description == checksum; + var reqCheck = diffPair.Entity.Description == null; return (dbCheck && reqCheck); } diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 545c009ae0..271e1baf09 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -261,7 +261,7 @@ void MockHooks(Mock> resourceDefinition) .Setup(rd => rd.BeforeRead(It.IsAny(), It.IsAny(), It.IsAny())) .Verifiable(); resourceDefinition - .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) + .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) .Returns, ResourcePipeline>((entityDiff, context) => entityDiff.Entities) .Verifiable(); resourceDefinition From 5ab6407e0334724e8d0f3f09663063e1d1277e85 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 11 Jun 2019 17:12:15 +0200 Subject: [PATCH 167/168] fix: inverse loading without inverse relationship attribute --- .../Data/DefaultEntityRepository.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index bfbbdc8c78..45d05d40eb 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -198,9 +198,17 @@ private void LoadInverseRelationships(object trackedRelationshipValue, Relations private bool IsHasOneRelationship(string internalRelationshipName, Type type) { - var relationshipAttr = _jsonApiContext.ResourceGraph.GetContextEntity(type).Relationships.Single(r => r.InternalRelationshipName == internalRelationshipName); - if (relationshipAttr is HasOneAttribute) return true; - return false; + var relationshipAttr = _jsonApiContext.ResourceGraph.GetContextEntity(type).Relationships.SingleOrDefault(r => r.InternalRelationshipName == internalRelationshipName); + if(relationshipAttr != null) + { + if (relationshipAttr is HasOneAttribute) return true; + return false; + } else + { + // relationshipAttr is null when we don't put a [RelationshipAttribute] on the inverse navigation property. + // In this case we use relfection to figure out what kind of relationship is pointing back. + return !(type.GetProperty(internalRelationshipName).PropertyType.Inherits(typeof(IEnumerable))); + } } From 6ccb1bb5ce2e9325f03377df7fd039a5cdda8f98 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 17 Jun 2019 11:42:38 +0200 Subject: [PATCH 168/168] fix: merge --- src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 134f22b187..dabf447efe 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -208,7 +208,6 @@ private void LoadInverseRelationships(object trackedRelationshipValue, Relations private bool IsHasOneRelationship(string internalRelationshipName, Type type) { var relationshipAttr = _jsonApiContext.ResourceGraph.GetContextEntity(type).Relationships.SingleOrDefault(r => r.InternalRelationshipName == internalRelationshipName); -<<<<<<< HEAD if(relationshipAttr != null) { if (relationshipAttr is HasOneAttribute) return true; @@ -216,16 +215,6 @@ private bool IsHasOneRelationship(string internalRelationshipName, Type type) } else { // relationshipAttr is null when we don't put a [RelationshipAttribute] on the inverse navigation property. -======= - if (relationshipAttr != null) - { - if (relationshipAttr is HasOneAttribute) return true; - return false; - } - else - { - // relationshipAttr is null when there is not put a [RelationshipAttribute] on the inverse navigation property. ->>>>>>> master // In this case we use relfection to figure out what kind of relationship is pointing back. return !(type.GetProperty(internalRelationshipName).PropertyType.Inherits(typeof(IEnumerable))); }