Feature request: improved hook support
See original GitHub issueDescription
The past weeks I have been trying to implement several features of our existing API using JsonApiDotNetCore. Starting out with custom services and repositories, I am now trying to make more use of hooks. One thing that I’m stuck at is the hook support for reading lists.
Consider the next method in DefaultResourceService
:
public virtual async Task<IEnumerable<TResource>> GetAsync()
{
_hookExecutor?.BeforeRead<TResource>(ResourcePipeline.Get);
var entityQuery = _repository.Get();
entityQuery = ApplyFilter(entityQuery);
entityQuery = ApplySort(entityQuery);
entityQuery = ApplyInclude(entityQuery);
entityQuery = ApplySelect(entityQuery);
if (!IsNull(_hookExecutor, entityQuery))
{
var entities = await _repository.ToListAsync(entityQuery);
_hookExecutor.AfterRead(entities, ResourcePipeline.Get);
entityQuery = _hookExecutor.OnReturn(entities, ResourcePipeline.Get).AsQueryable();
}
if (_options.IncludeTotalRecordCount)
_pageManager.TotalRecords = await _repository.CountAsync(entityQuery);
// pagination should be done last since it will execute the query
var pagedEntities = await ApplyPageQueryAsync(entityQuery);
return pagedEntities;
}
The first thing to notice is that whenever you start to use a hook (OnReturn in my case), paging no longer works server-side. This is unacceptable for large tables. I think that if the hook subscriber really needs the query executed, it should do so itself and live with the consequences.
Something else I found is that hooks cannot be generic. It’s unclear to me if that is a bug or by design. To illustrate, the following works:
services.AddScoped<ResourceDefinition<Video>, VideoResourceHookContainer>();
public class VideoResourceHookContainer : TypeEmbeddedIdResourceHookContainer<Video> { }
public class TypeEmbeddedIdResourceHookContainer<TResource>
: ResourceDefinition<TResource>
where TResource : class, IIdentifiable<long>
{
// ...
}
but the following does not:
services.AddScoped<ResourceDefinition<Video>, TypeEmbeddedIdResourceHookContainer<Video>>();
public class TypeEmbeddedIdResourceHookContainer<TResource>
: ResourceDefinition<TResource>
where TResource : class, IIdentifiable<long>
{
// ...
}
Finally, the documentation on the interfaces in IResourceHookExecutor.cs is wrong. For example, IReadHookExecutor contains: “Wrapper interface for all Before execution methods.” Also the file contains the misspelling “appropiate” multiple times.
Environment
Latest commit in develop branch.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:2
- Comments:12 (11 by maintainers)
Top GitHub Comments
Hi All, Yes i totally agree with Bart above. I’m working on a POC using JADNC 3.1.0 and have just started migrating it to the latest 4.0.0 alpha. Basic gets were working fine with paging out of the box as I had set the options.EnableResourceHooks to false. Without doing anything other than changing that option to true, all of a sudden a number of gets on large tables were trying to retrieve every record because of the queryable being enumerated to a list in the GetAsync method Bart highlighted above. I can see this potentially causing issues for a lot of people, especially as it seems hooks will need to be enabled and used in a lot of scenarios. As it stands right now, I cannot use this package with hooks enabled, without implementing a custom resource service overriding this method and using this for all of my entities. FYI, I totally understand the rationale for doing it this way in order to satisfy paging functionality after a hook may have further filtered or sorted the data, but I think this responsibility must be taken on by the developer themselves within the hook, though I acknowledge your concerns around this as well. I also like the idea of having hooks more granular so that the above would only occur if hooks were enabled, AND the OnReturn hook was actually implemented. Regard,
Closing this in favor of #934, which proposes a simpler, non-recursive design. Combined with recently added
IResourceDefinition
callbacks such asOnApplyFilter
,OnApplyIncludes
,OnApplySparseFieldSet
etc. I suspect it covers the major use cases.