Skip to content

Commit 2a06f30

Browse files
author
Bart Koelman
authored
Atomic operations (#9)
Updated to JsonApiDotNetCore 4.1.x and added atomic:operations support
1 parent 5ff61b6 commit 2a06f30

File tree

55 files changed

+3343
-65
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+3343
-65
lines changed

Directory.Build.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<NetCoreAppVersion>netcoreapp3.1</NetCoreAppVersion>
44
<AspNetCoreVersion>3.1.*</AspNetCoreVersion>
5-
<JsonApiDotNetCoreVersion>4.*</JsonApiDotNetCoreVersion>
5+
<JsonApiDotNetCoreVersion>4.1.*</JsonApiDotNetCoreVersion>
66
<MongoDBDriverVersion>2.11.*</MongoDBDriverVersion>
77
<CodeAnalysisRuleSet>$(SolutionDir)CodingGuidelines.ruleset</CodeAnalysisRuleSet>
88
</PropertyGroup>

JsonApiDotNetCore.MongoDb.sln.DotSettings

+1
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ $left$ = $right$;</s:String>
619619
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=B3D9EE6B4EC62A4F961EB15F9ADEC2C6/SearchPattern/@EntryValue">$collection$ == null || !$collection$.Any()</s:String>
620620
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=B3D9EE6B4EC62A4F961EB15F9ADEC2C6/Severity/@EntryValue">WARNING</s:String>
621621
<s:Boolean x:Key="/Default/UserDictionary/Words/=Assignee/@EntryIndexedValue">True</s:Boolean>
622+
<s:Boolean x:Key="/Default/UserDictionary/Words/=ffffffffffffffffffffffff/@EntryIndexedValue">True</s:Boolean>
622623
<s:Boolean x:Key="/Default/UserDictionary/Words/=linebreaks/@EntryIndexedValue">True</s:Boolean>
623624
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mongo/@EntryIndexedValue">True</s:Boolean>
624625
<s:Boolean x:Key="/Default/UserDictionary/Words/=playlists/@EntryIndexedValue">True</s:Boolean>

NuGet.config

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<packageSources>
4+
<add key="AppVeyor JsonApiDotNetCore" value="https://ci.appveyor.com/nuget/jsonapidotnetcore" />
5+
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
6+
</packageSources>
7+
</configuration>

appveyor.yml

-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ branches:
1212
nuget:
1313
disable_publish_on_pr: true
1414

15-
services:
16-
- mongodb
17-
1815
build_script:
1916
- pwsh: dotnet --version
2017
- pwsh: .\Build.ps1

src/Examples/GettingStarted/GettingStarted.csproj

-5
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@
33
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
44
</PropertyGroup>
55

6-
<ItemGroup>
7-
<PackageReference Include="JsonApiDotNetCore" Version="$(JsonApiDotNetCoreVersion)" />
8-
<PackageReference Include="MongoDB.Driver" Version="$(MongoDBDriverVersion)" />
9-
</ItemGroup>
10-
116
<ItemGroup>
127
<ProjectReference Include="..\..\JsonApiDotNetCore.MongoDb\JsonApiDotNetCore.MongoDb.csproj" />
138
</ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using JsonApiDotNetCore.AtomicOperations;
2+
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.Controllers;
4+
using JsonApiDotNetCore.Middleware;
5+
using JsonApiDotNetCore.Resources;
6+
using Microsoft.Extensions.Logging;
7+
8+
namespace JsonApiDotNetCoreMongoDbExample.Controllers
9+
{
10+
public sealed class OperationsController : JsonApiOperationsController
11+
{
12+
public OperationsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request,
13+
ITargetedFields targetedFields)
14+
: base(options, loggerFactory, processor, request, targetedFields)
15+
{
16+
}
17+
}
18+
}

src/Examples/JsonApiDotNetCoreMongoDbExample/JsonApiDotNetCoreMongoDbExample.csproj

-5
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@
33
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
44
</PropertyGroup>
55

6-
<ItemGroup>
7-
<PackageReference Include="JsonApiDotNetCore" Version="$(JsonApiDotNetCoreVersion)" />
8-
<PackageReference Include="MongoDB.Driver" Version="$(MongoDBDriverVersion)" />
9-
</ItemGroup>
10-
116
<ItemGroup>
127
<ProjectReference Include="..\..\JsonApiDotNetCore.MongoDb\JsonApiDotNetCore.MongoDb.csproj" />
138
</ItemGroup>

src/Examples/JsonApiDotNetCoreMongoDbExample/Startups/Startup.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Microsoft.AspNetCore.Hosting;
88
using Microsoft.Extensions.Configuration;
99
using Microsoft.Extensions.DependencyInjection;
10-
using Microsoft.Extensions.DependencyInjection.Extensions;
1110
using MongoDB.Driver;
1211
using Newtonsoft.Json;
1312
using Newtonsoft.Json.Converters;
@@ -28,9 +27,7 @@ public override void ConfigureServices(IServiceCollection services)
2827
{
2928
services.AddSingleton<ISystemClock, SystemClock>();
3029

31-
// TryAddSingleton will only register the IMongoDatabase if there is no
32-
// previously registered instance - will make tests use individual dbs
33-
services.TryAddSingleton(_ =>
30+
services.AddSingleton(_ =>
3431
{
3532
var client = new MongoClient(_configuration.GetSection("DatabaseSettings:ConnectionString").Value);
3633
return client.GetDatabase(_configuration.GetSection("DatabaseSettings:Database").Value);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using JetBrains.Annotations;
4+
using JsonApiDotNetCore.AtomicOperations;
5+
using JsonApiDotNetCore.MongoDb.Repositories;
6+
7+
namespace JsonApiDotNetCore.MongoDb.AtomicOperations
8+
{
9+
/// <inheritdoc />
10+
[PublicAPI]
11+
public sealed class MongoDbTransaction : IOperationsTransaction
12+
{
13+
private readonly IMongoDataAccess _mongoDataAccess;
14+
private readonly bool _ownsTransaction;
15+
16+
/// <inheritdoc />
17+
public string TransactionId => _mongoDataAccess.TransactionId;
18+
19+
public MongoDbTransaction(IMongoDataAccess mongoDataAccess, bool ownsTransaction)
20+
{
21+
ArgumentGuard.NotNull(mongoDataAccess, nameof(mongoDataAccess));
22+
23+
_mongoDataAccess = mongoDataAccess;
24+
_ownsTransaction = ownsTransaction;
25+
}
26+
27+
/// <inheritdoc />
28+
public Task BeforeProcessOperationAsync(CancellationToken cancellationToken)
29+
{
30+
return Task.CompletedTask;
31+
}
32+
33+
/// <inheritdoc />
34+
public Task AfterProcessOperationAsync(CancellationToken cancellationToken)
35+
{
36+
return Task.CompletedTask;
37+
}
38+
39+
/// <inheritdoc />
40+
public async Task CommitAsync(CancellationToken cancellationToken)
41+
{
42+
if (_ownsTransaction)
43+
{
44+
await _mongoDataAccess.ActiveSession.CommitTransactionAsync(cancellationToken);
45+
}
46+
}
47+
48+
/// <inheritdoc />
49+
public async ValueTask DisposeAsync()
50+
{
51+
if (_ownsTransaction)
52+
{
53+
await _mongoDataAccess.DisposeAsync();
54+
}
55+
}
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using JsonApiDotNetCore.AtomicOperations;
4+
using JsonApiDotNetCore.MongoDb.Repositories;
5+
6+
namespace JsonApiDotNetCore.MongoDb.AtomicOperations
7+
{
8+
/// <summary>
9+
/// Provides transaction support for atomic:operation requests using MongoDB.
10+
/// </summary>
11+
public sealed class MongoDbTransactionFactory : IOperationsTransactionFactory
12+
{
13+
private readonly IMongoDataAccess _mongoDataAccess;
14+
15+
public MongoDbTransactionFactory(IMongoDataAccess mongoDataAccess)
16+
{
17+
ArgumentGuard.NotNull(mongoDataAccess, nameof(mongoDataAccess));
18+
19+
_mongoDataAccess = mongoDataAccess;
20+
}
21+
22+
/// <inheritdoc />
23+
public async Task<IOperationsTransaction> BeginTransactionAsync(CancellationToken cancellationToken)
24+
{
25+
bool transactionCreated = await CreateOrJoinTransactionAsync(cancellationToken);
26+
return new MongoDbTransaction(_mongoDataAccess, transactionCreated);
27+
}
28+
29+
private async Task<bool> CreateOrJoinTransactionAsync(CancellationToken cancellationToken)
30+
{
31+
_mongoDataAccess.ActiveSession ??= await _mongoDataAccess.MongoDatabase.Client.StartSessionAsync(cancellationToken: cancellationToken);
32+
33+
if (_mongoDataAccess.ActiveSession.IsInTransaction)
34+
{
35+
return false;
36+
}
37+
38+
_mongoDataAccess.ActiveSession.StartTransaction();
39+
return true;
40+
}
41+
}
42+
}

src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using JetBrains.Annotations;
2+
using JsonApiDotNetCore.AtomicOperations;
3+
using JsonApiDotNetCore.MongoDb.AtomicOperations;
4+
using JsonApiDotNetCore.MongoDb.Repositories;
25
using JsonApiDotNetCore.MongoDb.Serialization.Building;
36
using JsonApiDotNetCore.Serialization.Building;
47
using Microsoft.Extensions.DependencyInjection;
@@ -13,6 +16,8 @@ public static class ServiceCollectionExtensions
1316
[PublicAPI]
1417
public static IServiceCollection AddJsonApiMongoDb(this IServiceCollection services)
1518
{
19+
services.AddScoped<IMongoDataAccess, MongoDataAccess>();
20+
services.AddScoped<IOperationsTransactionFactory, MongoDbTransactionFactory>();
1621
services.AddScoped<IResourceObjectBuilder, IgnoreRelationshipsResponseResourceObjectBuilder>();
1722

1823
return services;

src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<VersionPrefix>4.0.0</VersionPrefix>
4+
<VersionPrefix>4.1.0</VersionPrefix>
55
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
66
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
77
</PropertyGroup>
@@ -15,6 +15,7 @@
1515
<PublishRepositoryUrl>true</PublishRepositoryUrl>
1616
<EmbedUntrackedSources>true</EmbedUntrackedSources>
1717
<DebugType>embedded</DebugType>
18+
<Version>4.1.0-pre</Version>
1819
</PropertyGroup>
1920

2021
<ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using MongoDB.Driver;
3+
4+
namespace JsonApiDotNetCore.MongoDb.Repositories
5+
{
6+
/// <summary>
7+
/// Provides access to the MongoDB Driver and the optionally active session.
8+
/// </summary>
9+
public interface IMongoDataAccess : IAsyncDisposable
10+
{
11+
/// <summary>
12+
/// Provides access to the underlying MongoDB database, which data changes can be applied on.
13+
/// </summary>
14+
IMongoDatabase MongoDatabase { get; }
15+
16+
/// <summary>
17+
/// Provides access to the active session, if any.
18+
/// </summary>
19+
IClientSessionHandle ActiveSession { get; set; }
20+
21+
/// <summary>
22+
/// Identifies the current transaction, if any.
23+
/// </summary>
24+
string TransactionId { get; }
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Threading.Tasks;
2+
using MongoDB.Driver;
3+
4+
namespace JsonApiDotNetCore.MongoDb.Repositories
5+
{
6+
/// <inheritdoc />
7+
public sealed class MongoDataAccess : IMongoDataAccess
8+
{
9+
/// <inheritdoc />
10+
public IMongoDatabase MongoDatabase { get; }
11+
12+
/// <inheritdoc />
13+
public IClientSessionHandle ActiveSession { get; set; }
14+
15+
/// <inheritdoc />
16+
public string TransactionId => ActiveSession != null && ActiveSession.IsInTransaction ? ActiveSession.GetHashCode().ToString() : null;
17+
18+
public MongoDataAccess(IMongoDatabase mongoDatabase)
19+
{
20+
ArgumentGuard.NotNull(mongoDatabase, nameof(mongoDatabase));
21+
22+
MongoDatabase = mongoDatabase;
23+
}
24+
25+
/// <inheritdoc />
26+
public async ValueTask DisposeAsync()
27+
{
28+
if (ActiveSession != null)
29+
{
30+
if (ActiveSession.IsInTransaction)
31+
{
32+
await ActiveSession.AbortTransactionAsync();
33+
}
34+
35+
ActiveSession.Dispose();
36+
ActiveSession = null;
37+
}
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)