-
-
Notifications
You must be signed in to change notification settings - Fork 159
Open
Labels
Milestone
@bart-degreed
Description
Over time, several users have been asking for an alternative to adding attributes like [Attr] [HasOne] etc. EF Core provides both inline attributes, as well as a fluent API. This issue tracks the design of a fluent API for JsonApiDotNetCore.
First, let's zoom out a bit and see what happens currently in services.AddJsonApi():
- set global options, optional
- service/definition registration (assembly scan), optional
- resource graph building, one or both of:
- from DbContext (scan attributes of types in model)
- manually per resource (scan attributes of single type)
In my opinion, adding a fluent API should be part of the last step, which currently looks like:
services.AddJsonApi( options => options.Namespace = "api/v1", resources: builder => { builder.AddResource<WorkItem>(); // <-- manual per-resource registration });
And I think we should replace that with support for this, similar to EF Core:
services.AddJsonApi( options => options.Namespace = "api/v1", resources: builder => { builder.Resource<WorkItem>() .Attribute(workItem => workItem.Title) .PublicName("work-title") .Capabilities(AttrCapabilities.AllowFilter); builder.Resource<WorkItem>() .HasOne(workItem => workItem.Project) .PublicName("assigned-to-project"); });
How would that work?
- When
builder.Resource<>()
is called and the resource is not part of the graph, first it runs existing attribute scanning logic - Next, it processes the chained methods, overriding the registration that was added from attributes
For reference, the public API of builders could look something like this:
public class ResourceTypeBuilder<TResource> { public ResourceTypeBuilder<TResource> Attribute(Func<TResource, object> propertySelector) { return this; } public ResourceTypeBuilder<TResource> PublicName(string publicName) { return this; } public ResourceTypeBuilder<TResource> Capabilities(AttrCapabilities capabilities) { return this; } public HasOneRelationshipBuilder<TResource> HasOne(Func<TResource, object> relationshipSelector) { return new HasOneRelationshipBuilder<TResource>(); } } public class HasOneRelationshipBuilder<TResource> { public HasOneRelationshipBuilder<TResource> PublicName(string publicName) { return this; } }