Bulk/batch
since v4.1
The Atomic Operations JSON:API extension defines how to perform multiple write operations in a linear and atomic manner.
Clients can send an array of operations in a single request. JsonApiDotNetCore guarantees that those operations will be processed in order and will either completely succeed or fail together.
On failure, the zero-based index of the failing operation is returned in the error.source.pointer
field of the error response.
Usage
To enable operations, add a controller to your project that inherits from JsonApiOperationsController
or BaseJsonApiOperationsController
:
public sealed class OperationsController : JsonApiOperationsController
{
public OperationsController(IJsonApiOptions options, IResourceGraph resourceGraph,
ILoggerFactory loggerFactory, IOperationsProcessor processor,
IJsonApiRequest request, ITargetedFields targetedFields)
: base(options, resourceGraph, loggerFactory, processor, request, targetedFields)
{
}
}
You'll need to send the next Content-Type in a POST request for operations:
application/vnd.api+json; ext="https://jsonapi.org/ext/atomic"
Local IDs
Local IDs (lid) can be used to associate resources that have not yet been assigned an ID. The next example creates two resources and sets a relationship between them:
POST http://localhost/api/operations HTTP/1.1
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [
{
"op": "add",
"data": {
"type": "musicTracks",
"lid": "id-for-i-will-survive",
"attributes": {
"title": "I will survive"
}
}
},
{
"op": "add",
"data": {
"type": "performers",
"lid": "id-for-gloria-gaynor",
"attributes": {
"artistName": "Gloria Gaynor"
}
}
},
{
"op": "update",
"ref": {
"type": "musicTracks",
"lid": "id-for-i-will-survive",
"relationship": "performers"
},
"data": [
{
"type": "performers",
"lid": "id-for-gloria-gaynor"
}
]
}
]
}
For example requests, see our suite of tests in JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations.
Configuration
The maximum number of operations per request defaults to 10, which you can change at startup:
// Program.cs
builder.Services.AddJsonApi(options => options.MaximumOperationsPerRequest = 250);
Or, if you want to allow unconstrained, set it to null
instead.
Multiple controllers
You can register multiple operations controllers using custom routes, for example:
[DisableRoutingConvention, Route("/operations/musicTracks/create")]
public sealed class CreateMusicTrackOperationsController : JsonApiOperationsController
{
public override async Task<IActionResult> PostOperationsAsync(
IList<OperationContainer> operations, CancellationToken cancellationToken)
{
AssertOnlyCreatingMusicTracks(operations);
return await base.PostOperationsAsync(operations, cancellationToken);
}
}
Limitations
For our atomic:operations implementation, the next limitations apply:
- The
ref.href
field cannot be used. Use type/id or type/lid instead. - You cannot both assign and reference the same local ID in a single operation.
- All repositories used in an operations request must implement
IRepositorySupportsTransaction
and participate in the same transaction. - If you're not using Entity Framework Core, you'll need to implement and register
IOperationsTransactionFactory
yourself.