Skip to content

Commit d929bcb

Browse files
author
Bart Koelman
committed
Update docs on layer overview and resource definitions.
1 parent b94bb1b commit d929bcb

File tree

4 files changed

+52
-42
lines changed

4 files changed

+52
-42
lines changed
Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,42 @@
11
# Layer Overview
22

3-
By default, data retrieval is distributed across three layers:
3+
By default, data access flows through the next three layers:
44

55
```
66
JsonApiController (required)
7+
JsonApiResourceService : IResourceService
8+
EntityFrameworkCoreRepository : IResourceRepository
9+
```
710

8-
+-- JsonApiResourceService : IResourceService
11+
Aside from these pluggable endpoint-oriented layers, we provide a resource-oriented extensibility point:
912

10-
+-- EntityFrameworkCoreRepository : IResourceRepository
13+
```
14+
JsonApiResourceDefinition : IResourceDefinition
1115
```
1216

13-
Customization can be done at any of these layers. However, it is recommended that you make your customizations at the service or the repository layer when possible, to keep the controllers free of unnecessary logic.
14-
You can use the following as a general rule of thumb for where to put business logic:
17+
Resource definition callbacks are invoked from the built-in resource service/repository layers, as well as from the serializer.
18+
For example, `IResourceDefinition.OnSerialize` is invoked whenever a resource is sent back to the client, irrespective of the endpoint.
19+
Likewise, `IResourceDefinition.OnSetToOneRelationshipAsync` is called from a patch-resource-with-relationships endpoint, as well as from patch-relationship.
1520

16-
- `Controller`: simple validation logic that should result in the return of specific HTTP status codes, such as model validation
17-
- `IResourceService`: advanced business logic and replacement of data access mechanisms
18-
- `IResourceRepository`: custom logic that builds on the Entity Framework Core APIs
21+
Customization can be done at any of these extensibility points. It is usually sufficient to place your business logic in a resource definition, but depending
22+
on your needs, you may want to replace other parts by deriving from the built-in classes and override virtual methods or call their protected base methods.
1923

20-
## Replacing Services
24+
## Replacing injected services
2125

22-
**Note:** If you are using auto-discovery, resource services and repositories will be automatically registered for you.
26+
**Note:** If you are using auto-discovery, then resource services, repositories and resource definitions will be automatically registered for you.
2327

24-
Replacing services and repositories is done on a per-resource basis and can be done through dependency injection in your Startup.cs file.
28+
Replacing built-in services is done on a per-resource basis and can be done through dependency injection in your Startup.cs file.
29+
For convenience, extension methods are provided to register layers on all their implemented interfaces.
2530

2631
```c#
2732
// Startup.cs
2833
public void ConfigureServices(IServiceCollection services)
2934
{
30-
services.AddScoped<ProductService, IResourceService<Product>();
31-
services.AddScoped<ProductRepository, IResourceRepository<Product>>();
35+
services.AddResourceService<ProductService>();
36+
services.AddResourceRepository<ProductRepository>();
37+
services.AddResourceDefinition<ProductDefinition>();
38+
39+
services.AddScoped<IResourceFactory, CustomResourceFactory>();
40+
services.AddScoped<IJsonApiSerializerFactory, CustomResponseSerializerFactory>();
3241
}
3342
```

docs/usage/extensibility/resource-definitions.md

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
# Resource Definitions
22

3-
In order to improve the developer experience, we have introduced a type that makes
4-
common modifications to the default API behavior easier. Resource definitions were first introduced in v2.3.4.
3+
_since v2.3.4_
54

6-
Resource definitions are resolved from the dependency injection container, so you can inject dependencies in their constructor.
5+
Resource definitions provide a resource-oriented way to handle custom business logic (irrespective of the originating endpoint).
76

8-
## Customizing query clauses
7+
They are resolved from the dependency injection container, so you can inject dependencies in their constructor.
8+
9+
**Note:** Prior to the introduction of auto-discovery (in v3), you needed to register the
10+
`ResourceDefinition` on the container yourself:
11+
12+
```c#
13+
services.AddScoped<ResourceDefinition<Product>, ProductResource>();
14+
```
15+
16+
## Customizing queries
917

1018
_since v4.0_
1119

@@ -21,7 +29,7 @@ from Entity Framework Core `IQueryable` execution.
2129
There are some cases where you want attributes (or relationships) conditionally excluded from your resource response.
2230
For example, you may accept some sensitive data that should only be exposed to administrators after creation.
2331

24-
Note: to exclude attributes unconditionally, use `[Attr(Capabilities = ~AttrCapabilities.AllowView)]`.
32+
Note: to exclude attributes unconditionally, use `[Attr(Capabilities = ~AttrCapabilities.AllowView)]` on a resource class property.
2533

2634
```c#
2735
public class UserDefinition : JsonApiResourceDefinition<User>
@@ -78,7 +86,7 @@ Content-Type: application/vnd.api+json
7886
}
7987
```
8088

81-
## Default sort order
89+
### Default sort order
8290

8391
You can define the default sort order if no `sort` query string parameter is provided.
8492

@@ -106,7 +114,7 @@ public class AccountDefinition : JsonApiResourceDefinition<Account>
106114
}
107115
```
108116

109-
## Enforce page size
117+
### Enforce page size
110118

111119
You may want to enforce pagination on large database tables.
112120

@@ -137,9 +145,9 @@ public class AccessLogDefinition : JsonApiResourceDefinition<AccessLog>
137145
}
138146
```
139147

140-
## Exclude soft-deleted resources
148+
### Change filters
141149

142-
Soft-deletion sets `IsSoftDeleted` to `true` instead of actually deleting the record, so you may want to always filter them out.
150+
The next example filters out `Account` resources that are suspended.
143151

144152
```c#
145153
public class AccountDefinition : JsonApiResourceDefinition<Account>
@@ -153,23 +161,25 @@ public class AccountDefinition : JsonApiResourceDefinition<Account>
153161
{
154162
var resourceContext = ResourceGraph.GetResourceContext<Account>();
155163

156-
var isSoftDeletedAttribute =
164+
var isSuspendedAttribute =
157165
resourceContext.Attributes.Single(account =>
158-
account.Property.Name == nameof(Account.IsSoftDeleted));
166+
account.Property.Name == nameof(Account.IsSuspended));
159167

160-
var isNotSoftDeleted = new ComparisonExpression(ComparisonOperator.Equals,
161-
new ResourceFieldChainExpression(isSoftDeletedAttribute),
168+
var isNotSuspended = new ComparisonExpression(ComparisonOperator.Equals,
169+
new ResourceFieldChainExpression(isSuspendedAttribute),
162170
new LiteralConstantExpression(bool.FalseString));
163171

164172
return existingFilter == null
165-
? (FilterExpression) isNotSoftDeleted
173+
? (FilterExpression) isNotSuspended
166174
: new LogicalExpression(LogicalOperator.And,
167-
new[] { isNotSoftDeleted, existingFilter });
175+
new[] { isNotSuspended, existingFilter });
168176
}
169177
}
170178
```
171179

172-
## Block including related resources
180+
### Block including related resources
181+
182+
In the example below, an error is returned when a user tries to include the manager of an employee.
173183

174184
```c#
175185
public class EmployeeDefinition : JsonApiResourceDefinition<Employee>
@@ -196,14 +206,14 @@ public class EmployeeDefinition : JsonApiResourceDefinition<Employee>
196206
}
197207
```
198208

199-
## Custom query string parameters
209+
### Custom query string parameters
200210

201211
_since v3_
202212

203213
You can define additional query string parameters with the LINQ expression that should be used.
204214
If the key is present in a query string, the supplied LINQ expression will be added to the database query.
205215

206-
Note this directly influences the Entity Framework Core `IQueryable`. As opposed to using `OnApplyFilter`, this enables the full range of EF Core functionality.
216+
Note this directly influences the Entity Framework Core `IQueryable`. As opposed to using `OnApplyFilter`, this enables the full range of EF Core operators.
207217
But it only works on primary resource endpoints (for example: /articles, but not on /blogs/1/articles or /blogs?include=articles).
208218

209219
```c#
@@ -237,12 +247,3 @@ public class ItemDefinition : JsonApiResourceDefinition<Item>
237247
}
238248
}
239249
```
240-
241-
## Using Resource Definitions prior to v3
242-
243-
Prior to the introduction of auto-discovery, you needed to register the
244-
`ResourceDefinition` on the container yourself:
245-
246-
```c#
247-
services.AddScoped<ResourceDefinition<Item>, ItemResource>();
248-
```

docs/usage/reading/filtering.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ GET /articles?filter[caption]=tech&filter=expr:equals(caption,'cooking')) HTTP/1
119119

120120
There are multiple ways you can add custom filters:
121121

122-
1. Implementing `IResourceDefinition.OnApplyFilter` (see [here](~/usage/extensibility/resource-definitions.md#exclude-soft-deleted-resources)) and inject `IRequestQueryStringAccessor`, which works at all depths, but filter operations are constrained to what `FilterExpression` provides
122+
1. Implementing `IResourceDefinition.OnApplyFilter` (see [here](~/usage/extensibility/resource-definitions.md#change-filters)) and inject `IRequestQueryStringAccessor`, which works at all depths, but filter operations are constrained to what `FilterExpression` provides
123123
2. Implementing `IResourceDefinition.OnRegisterQueryableHandlersForQueryStringParameters` as described [here](~/usage/extensibility/resource-definitions.md#custom-query-string-parameters), which enables the full range of `IQueryable<T>` functionality, but only works on primary endpoints
124124
3. Add an implementation of `IQueryConstraintProvider` to supply additional `FilterExpression`s, which are combined with existing filters using AND operator
125125
4. Override `EntityFrameworkCoreRepository.ApplyQueryLayer` to adapt the `IQueryable<T>` expression just before execution

docs/usage/resources/relationships.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class Person : Identifiable
2929

3030
## HasManyThrough
3131

32-
Currently, Entity Framework Core [does not support](https://github.com/aspnet/EntityFrameworkCore/issues/1368) many-to-many relationships without a join entity.
32+
Earlier versions of Entity Framework Core (up to v5) [did not support](https://github.com/aspnet/EntityFrameworkCore/issues/1368) many-to-many relationships without a join entity.
3333
For this reason, we have decided to fill this gap by allowing applications to declare a relationship as `HasManyThrough`.
3434
JsonApiDotNetCore will expose this relationship to the client the same way as any other `HasMany` attribute.
3535
However, under the covers it will use the join type and Entity Framework Core's APIs to get and set the relationship.

0 commit comments

Comments
 (0)