Skip to content

Feature request: improved hook support #623

Closed
@bart-degreed

Description

@bart-degreed

Description

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions