-
-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into merge-master-dapper-into-openapi
- Loading branch information
Showing
133 changed files
with
14,008 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
src/Examples/DapperExample/AtomicOperations/AmbientTransaction.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using System.Data.Common; | ||
using JsonApiDotNetCore; | ||
using JsonApiDotNetCore.AtomicOperations; | ||
|
||
namespace DapperExample.AtomicOperations; | ||
|
||
/// <summary> | ||
/// Represents an ADO.NET transaction in a JSON:API atomic:operations request. | ||
/// </summary> | ||
internal sealed class AmbientTransaction : IOperationsTransaction | ||
{ | ||
private readonly AmbientTransactionFactory _owner; | ||
|
||
public DbTransaction Current { get; } | ||
|
||
/// <inheritdoc /> | ||
public string TransactionId { get; } | ||
|
||
public AmbientTransaction(AmbientTransactionFactory owner, DbTransaction current, Guid transactionId) | ||
{ | ||
ArgumentGuard.NotNull(owner); | ||
ArgumentGuard.NotNull(current); | ||
|
||
_owner = owner; | ||
Current = current; | ||
TransactionId = transactionId.ToString(); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task BeforeProcessOperationAsync(CancellationToken cancellationToken) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task AfterProcessOperationAsync(CancellationToken cancellationToken) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task CommitAsync(CancellationToken cancellationToken) | ||
{ | ||
return Current.CommitAsync(cancellationToken); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async ValueTask DisposeAsync() | ||
{ | ||
DbConnection? connection = Current.Connection; | ||
|
||
await Current.DisposeAsync(); | ||
|
||
if (connection != null) | ||
{ | ||
await connection.DisposeAsync(); | ||
} | ||
|
||
_owner.Detach(this); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
src/Examples/DapperExample/AtomicOperations/AmbientTransactionFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using System.Data.Common; | ||
using DapperExample.TranslationToSql.DataModel; | ||
using JsonApiDotNetCore; | ||
using JsonApiDotNetCore.AtomicOperations; | ||
using JsonApiDotNetCore.Configuration; | ||
|
||
namespace DapperExample.AtomicOperations; | ||
|
||
/// <summary> | ||
/// Provides transaction support for JSON:API atomic:operation requests using ADO.NET. | ||
/// </summary> | ||
public sealed class AmbientTransactionFactory : IOperationsTransactionFactory | ||
{ | ||
private readonly IJsonApiOptions _options; | ||
private readonly IDataModelService _dataModelService; | ||
|
||
internal AmbientTransaction? AmbientTransaction { get; private set; } | ||
|
||
public AmbientTransactionFactory(IJsonApiOptions options, IDataModelService dataModelService) | ||
{ | ||
ArgumentGuard.NotNull(options); | ||
ArgumentGuard.NotNull(dataModelService); | ||
|
||
_options = options; | ||
_dataModelService = dataModelService; | ||
} | ||
|
||
internal async Task<AmbientTransaction> BeginTransactionAsync(CancellationToken cancellationToken) | ||
{ | ||
var instance = (IOperationsTransactionFactory)this; | ||
|
||
IOperationsTransaction transaction = await instance.BeginTransactionAsync(cancellationToken); | ||
return (AmbientTransaction)transaction; | ||
} | ||
|
||
async Task<IOperationsTransaction> IOperationsTransactionFactory.BeginTransactionAsync(CancellationToken cancellationToken) | ||
{ | ||
if (AmbientTransaction != null) | ||
{ | ||
throw new InvalidOperationException("Cannot start transaction because another transaction is already active."); | ||
} | ||
|
||
DbConnection dbConnection = _dataModelService.CreateConnection(); | ||
|
||
try | ||
{ | ||
await dbConnection.OpenAsync(cancellationToken); | ||
|
||
DbTransaction transaction = _options.TransactionIsolationLevel != null | ||
? await dbConnection.BeginTransactionAsync(_options.TransactionIsolationLevel.Value, cancellationToken) | ||
: await dbConnection.BeginTransactionAsync(cancellationToken); | ||
|
||
var transactionId = Guid.NewGuid(); | ||
AmbientTransaction = new AmbientTransaction(this, transaction, transactionId); | ||
|
||
return AmbientTransaction; | ||
} | ||
catch (DbException) | ||
{ | ||
await dbConnection.DisposeAsync(); | ||
throw; | ||
} | ||
} | ||
|
||
internal void Detach(AmbientTransaction ambientTransaction) | ||
{ | ||
ArgumentGuard.NotNull(ambientTransaction); | ||
|
||
if (AmbientTransaction != null && AmbientTransaction == ambientTransaction) | ||
{ | ||
AmbientTransaction = null; | ||
} | ||
else | ||
{ | ||
throw new InvalidOperationException("Failed to detach ambient transaction."); | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/Examples/DapperExample/Controllers/OperationsController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using JsonApiDotNetCore.AtomicOperations; | ||
using JsonApiDotNetCore.Configuration; | ||
using JsonApiDotNetCore.Controllers; | ||
using JsonApiDotNetCore.Middleware; | ||
using JsonApiDotNetCore.Resources; | ||
|
||
namespace DapperExample.Controllers; | ||
|
||
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) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
<PropertyGroup> | ||
<TargetFramework>$(TargetFrameworkName)</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\JsonApiDotNetCore\JsonApiDotNetCore.csproj" /> | ||
<ProjectReference Include="..\..\JsonApiDotNetCore.SourceGenerators\JsonApiDotNetCore.SourceGenerators.csproj" OutputItemType="Analyzer" | ||
ReferenceOutputAssembly="false" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Dapper" Version="$(DapperVersion)" /> | ||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="$(EntityFrameworkCoreVersion)" /> | ||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(EntityFrameworkCoreVersion)" /> | ||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(NpgsqlVersion)" /> | ||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="$(EntityFrameworkCoreVersion)" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
using DapperExample.Models; | ||
using JetBrains.Annotations; | ||
using JsonApiDotNetCore; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.EntityFrameworkCore.Metadata; | ||
|
||
// @formatter:wrap_chained_method_calls chop_always | ||
|
||
namespace DapperExample.Data; | ||
|
||
[UsedImplicitly(ImplicitUseTargetFlags.Members)] | ||
public sealed class AppDbContext : DbContext | ||
{ | ||
private readonly IConfiguration _configuration; | ||
|
||
public DbSet<TodoItem> TodoItems => Set<TodoItem>(); | ||
public DbSet<Person> People => Set<Person>(); | ||
public DbSet<LoginAccount> LoginAccounts => Set<LoginAccount>(); | ||
public DbSet<AccountRecovery> AccountRecoveries => Set<AccountRecovery>(); | ||
public DbSet<Tag> Tags => Set<Tag>(); | ||
public DbSet<RgbColor> RgbColors => Set<RgbColor>(); | ||
|
||
public AppDbContext(DbContextOptions<AppDbContext> options, IConfiguration configuration) | ||
: base(options) | ||
{ | ||
ArgumentGuard.NotNull(configuration); | ||
|
||
_configuration = configuration; | ||
} | ||
|
||
protected override void OnModelCreating(ModelBuilder builder) | ||
{ | ||
builder.Entity<Person>() | ||
.HasMany(person => person.AssignedTodoItems) | ||
.WithOne(todoItem => todoItem.Assignee); | ||
|
||
builder.Entity<Person>() | ||
.HasMany(person => person.OwnedTodoItems) | ||
.WithOne(todoItem => todoItem.Owner); | ||
|
||
builder.Entity<Person>() | ||
.HasOne(person => person.Account) | ||
.WithOne(loginAccount => loginAccount.Person) | ||
.HasForeignKey<Person>("AccountId"); | ||
|
||
builder.Entity<LoginAccount>() | ||
.HasOne(loginAccount => loginAccount.Recovery) | ||
.WithOne(accountRecovery => accountRecovery.Account) | ||
.HasForeignKey<LoginAccount>("RecoveryId"); | ||
|
||
builder.Entity<Tag>() | ||
.HasOne(tag => tag.Color) | ||
.WithOne(rgbColor => rgbColor.Tag) | ||
.HasForeignKey<RgbColor>("TagId"); | ||
|
||
var databaseProvider = _configuration.GetValue<DatabaseProvider>("DatabaseProvider"); | ||
|
||
if (databaseProvider != DatabaseProvider.SqlServer) | ||
{ | ||
// In this example project, all cascades happen in the database, but SQL Server doesn't support that very well. | ||
AdjustDeleteBehaviorForJsonApi(builder); | ||
} | ||
} | ||
|
||
private static void AdjustDeleteBehaviorForJsonApi(ModelBuilder builder) | ||
{ | ||
foreach (IMutableForeignKey foreignKey in builder.Model.GetEntityTypes() | ||
.SelectMany(entityType => entityType.GetForeignKeys())) | ||
{ | ||
if (foreignKey.DeleteBehavior == DeleteBehavior.ClientSetNull) | ||
{ | ||
foreignKey.DeleteBehavior = DeleteBehavior.SetNull; | ||
} | ||
|
||
if (foreignKey.DeleteBehavior == DeleteBehavior.ClientCascade) | ||
{ | ||
foreignKey.DeleteBehavior = DeleteBehavior.Cascade; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.