Skip to content

Commit 23da6d0

Browse files
authored
Merge pull request #1388 from json-api-dotnet/merge-master-dapper-into-openapi
Merge master (dapper example) into openapi
2 parents 65a356f + a3975b7 commit 23da6d0

File tree

133 files changed

+14008
-71
lines changed

Some content is hidden

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

133 files changed

+14008
-71
lines changed

.config/dotnet-tools.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"isRoot": true,
44
"tools": {
55
"jetbrains.resharper.globaltools": {
6-
"version": "2023.2.2",
6+
"version": "2023.2.3",
77
"commands": [
88
"jb"
99
]
@@ -21,7 +21,7 @@
2121
]
2222
},
2323
"docfx": {
24-
"version": "2.71.1",
24+
"version": "2.72.1",
2525
"commands": [
2626
"docfx"
2727
]

Directory.Build.props

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,21 @@
3030
<CSharpGuidelinesAnalyzerVersion>3.8.*</CSharpGuidelinesAnalyzerVersion>
3131
<CodeAnalysisVersion>4.7.*</CodeAnalysisVersion>
3232
<CoverletVersion>6.0.*</CoverletVersion>
33+
<DapperVersion>2.1.*</DapperVersion>
3334
<DateOnlyTimeOnlyVersion>2.1.*</DateOnlyTimeOnlyVersion>
3435
<EntityFrameworkCoreVersion>7.0.*</EntityFrameworkCoreVersion>
3536
<FluentAssertionsVersion>6.12.*</FluentAssertionsVersion>
3637
<GitHubActionsTestLoggerVersion>2.3.*</GitHubActionsTestLoggerVersion>
3738
<InheritDocVersion>1.3.*</InheritDocVersion>
38-
<JetBrainsAnnotationsVersion>2023.2.*</JetBrainsAnnotationsVersion>
39+
<JetBrainsAnnotationsVersion>2023.3.*</JetBrainsAnnotationsVersion>
3940
<MicrosoftApiClientVersion>7.0.*</MicrosoftApiClientVersion>
4041
<NSwagApiClientVersion>13.20.*</NSwagApiClientVersion>
4142
<NewtonsoftJsonVersion>13.0.*</NewtonsoftJsonVersion>
4243
<NpgsqlVersion>7.0.*</NpgsqlVersion>
4344
<SourceLinkVersion>1.1.*</SourceLinkVersion>
4445
<SwashbuckleVersion>6.5.*</SwashbuckleVersion>
4546
<SystemTextJsonVersion>7.0.*</SystemTextJsonVersion>
46-
<TestSdkVersion>17.7.*</TestSdkVersion>
47+
<TestSdkVersion>17.8.*</TestSdkVersion>
4748
<XunitVersion>2.5.*</XunitVersion>
4849
</PropertyGroup>
4950

JsonApiDotNetCore.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DatabasePerTenantExample",
5656
EndProject
5757
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnnotationTests", "test\AnnotationTests\AnnotationTests.csproj", "{24B0C12F-38CD-4245-8785-87BEFAD55B00}"
5858
EndProject
59+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperExample", "src\Examples\DapperExample\DapperExample.csproj", "{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}"
60+
EndProject
61+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperTests", "test\DapperTests\DapperTests.csproj", "{80E322F5-5F5D-4670-A30F-02D33C2C7900}"
62+
EndProject
5963
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore.OpenApi", "src\JsonApiDotNetCore.OpenApi\JsonApiDotNetCore.OpenApi.csproj", "{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}"
6064
EndProject
6165
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiTests", "test\OpenApiTests\OpenApiTests.csproj", "{B693DE14-BB28-496F-AB39-B4E674ABCA80}"
@@ -294,6 +298,30 @@ Global
294298
{24B0C12F-38CD-4245-8785-87BEFAD55B00}.Release|x64.Build.0 = Release|Any CPU
295299
{24B0C12F-38CD-4245-8785-87BEFAD55B00}.Release|x86.ActiveCfg = Release|Any CPU
296300
{24B0C12F-38CD-4245-8785-87BEFAD55B00}.Release|x86.Build.0 = Release|Any CPU
301+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
302+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
303+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Debug|x64.ActiveCfg = Debug|Any CPU
304+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Debug|x64.Build.0 = Debug|Any CPU
305+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Debug|x86.ActiveCfg = Debug|Any CPU
306+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Debug|x86.Build.0 = Debug|Any CPU
307+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
308+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Release|Any CPU.Build.0 = Release|Any CPU
309+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Release|x64.ActiveCfg = Release|Any CPU
310+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Release|x64.Build.0 = Release|Any CPU
311+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Release|x86.ActiveCfg = Release|Any CPU
312+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2}.Release|x86.Build.0 = Release|Any CPU
313+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
314+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Debug|Any CPU.Build.0 = Debug|Any CPU
315+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Debug|x64.ActiveCfg = Debug|Any CPU
316+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Debug|x64.Build.0 = Debug|Any CPU
317+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Debug|x86.ActiveCfg = Debug|Any CPU
318+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Debug|x86.Build.0 = Debug|Any CPU
319+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Release|Any CPU.ActiveCfg = Release|Any CPU
320+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Release|Any CPU.Build.0 = Release|Any CPU
321+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Release|x64.ActiveCfg = Release|Any CPU
322+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Release|x64.Build.0 = Release|Any CPU
323+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Release|x86.ActiveCfg = Release|Any CPU
324+
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Release|x86.Build.0 = Release|Any CPU
297325
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
298326
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|Any CPU.Build.0 = Debug|Any CPU
299327
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -389,6 +417,8 @@ Global
389417
{83FF097C-C8C6-477B-9FAB-DF99B84978B5} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
390418
{60334658-BE51-43B3-9C4D-F2BBF56C89CE} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
391419
{24B0C12F-38CD-4245-8785-87BEFAD55B00} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
420+
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
421+
{80E322F5-5F5D-4670-A30F-02D33C2C7900} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
392422
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
393423
{B693DE14-BB28-496F-AB39-B4E674ABCA80} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
394424
{5ADAA902-5A75-4ECB-B4B4-03291D63CE9C} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}

JsonApiDotNetCore.sln.DotSettings

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,8 +662,12 @@ $left$ = $right$;</s:String>
662662
<s:Boolean x:Key="/Default/UserDictionary/Words/=linebreaks/@EntryIndexedValue">True</s:Boolean>
663663
<s:Boolean x:Key="/Default/UserDictionary/Words/=Microservices/@EntryIndexedValue">True</s:Boolean>
664664
<s:Boolean x:Key="/Default/UserDictionary/Words/=navigations/@EntryIndexedValue">True</s:Boolean>
665+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Npgsql/@EntryIndexedValue">True</s:Boolean>
665666
<s:Boolean x:Key="/Default/UserDictionary/Words/=parallelize/@EntryIndexedValue">True</s:Boolean>
667+
<s:Boolean x:Key="/Default/UserDictionary/Words/=parameterless/@EntryIndexedValue">True</s:Boolean>
666668
<s:Boolean x:Key="/Default/UserDictionary/Words/=playlists/@EntryIndexedValue">True</s:Boolean>
669+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pomelo/@EntryIndexedValue">True</s:Boolean>
670+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postgre/@EntryIndexedValue">True</s:Boolean>
667671
<s:Boolean x:Key="/Default/UserDictionary/Words/=Rewriter/@EntryIndexedValue">True</s:Boolean>
668672
<s:Boolean x:Key="/Default/UserDictionary/Words/=Startups/@EntryIndexedValue">True</s:Boolean>
669673
<s:Boolean x:Key="/Default/UserDictionary/Words/=subdirectory/@EntryIndexedValue">True</s:Boolean>

docs/getting-started/faq.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,18 @@ Take a look at [JsonApiResourceService](https://github.com/json-api-dotnet/JsonA
145145

146146
You'll get a lot more out of the box if replacing at the repository level instead. You don't need to apply options or analyze query strings.
147147
And most resource definition callbacks are handled.
148-
That's because the built-in resource service translates all JSON:API aspects of the request into a database-agnostic data structure called `QueryLayer`.
148+
That's because the built-in resource service translates all JSON:API query aspects of the request into a database-agnostic data structure called `QueryLayer`.
149149
Now the hard part for you becomes reading that data structure and producing data access calls from that.
150-
If your data store provides a LINQ provider, you may reuse most of [QueryableBuilder](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryableBuilder.cs),
150+
If your data store provides a LINQ provider, you can probably reuse [QueryableBuilder](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryableBuilder.cs),
151151
which drives the translation into [System.Linq.Expressions](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/).
152-
Note however, that it also produces calls to `.Include("")`, which is an Entity Framework Core-specific extension method, so you'll likely need to prevent that from happening. There's an example [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs).
153-
We use a similar approach for accessing [MongoDB](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/blob/674889e037334e3f376550178ce12d0842d7560c/src/JsonApiDotNetCore.MongoDb/Queries/Internal/QueryableBuilding/MongoQueryableBuilder.cs).
152+
Note however, that it also produces calls to `.Include("")`, which is an Entity Framework Core-specific extension method, so you'll need to
153+
[prevent that from happening](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryLayerIncludeConverter.cs).
154+
155+
The example [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs) compiles and executes
156+
the LINQ query against an in-memory list of resources.
157+
For [MongoDB](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/blob/master/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs), we use the MongoDB LINQ provider.
158+
If there's no LINQ provider available, the example [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/Examples/DapperExample/Repositories/DapperRepository.cs) may be of help,
159+
which produces SQL and uses [Dapper](https://github.com/DapperLib/Dapper) for data access.
154160

155161
> [!TIP]
156162
> [ExpressionTreeVisualizer](https://github.com/zspitz/ExpressionTreeVisualizer) is very helpful in trying to debug LINQ expression trees!
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System.Data.Common;
2+
using JsonApiDotNetCore;
3+
using JsonApiDotNetCore.AtomicOperations;
4+
5+
namespace DapperExample.AtomicOperations;
6+
7+
/// <summary>
8+
/// Represents an ADO.NET transaction in a JSON:API atomic:operations request.
9+
/// </summary>
10+
internal sealed class AmbientTransaction : IOperationsTransaction
11+
{
12+
private readonly AmbientTransactionFactory _owner;
13+
14+
public DbTransaction Current { get; }
15+
16+
/// <inheritdoc />
17+
public string TransactionId { get; }
18+
19+
public AmbientTransaction(AmbientTransactionFactory owner, DbTransaction current, Guid transactionId)
20+
{
21+
ArgumentGuard.NotNull(owner);
22+
ArgumentGuard.NotNull(current);
23+
24+
_owner = owner;
25+
Current = current;
26+
TransactionId = transactionId.ToString();
27+
}
28+
29+
/// <inheritdoc />
30+
public Task BeforeProcessOperationAsync(CancellationToken cancellationToken)
31+
{
32+
return Task.CompletedTask;
33+
}
34+
35+
/// <inheritdoc />
36+
public Task AfterProcessOperationAsync(CancellationToken cancellationToken)
37+
{
38+
return Task.CompletedTask;
39+
}
40+
41+
/// <inheritdoc />
42+
public Task CommitAsync(CancellationToken cancellationToken)
43+
{
44+
return Current.CommitAsync(cancellationToken);
45+
}
46+
47+
/// <inheritdoc />
48+
public async ValueTask DisposeAsync()
49+
{
50+
DbConnection? connection = Current.Connection;
51+
52+
await Current.DisposeAsync();
53+
54+
if (connection != null)
55+
{
56+
await connection.DisposeAsync();
57+
}
58+
59+
_owner.Detach(this);
60+
}
61+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Data.Common;
2+
using DapperExample.TranslationToSql.DataModel;
3+
using JsonApiDotNetCore;
4+
using JsonApiDotNetCore.AtomicOperations;
5+
using JsonApiDotNetCore.Configuration;
6+
7+
namespace DapperExample.AtomicOperations;
8+
9+
/// <summary>
10+
/// Provides transaction support for JSON:API atomic:operation requests using ADO.NET.
11+
/// </summary>
12+
public sealed class AmbientTransactionFactory : IOperationsTransactionFactory
13+
{
14+
private readonly IJsonApiOptions _options;
15+
private readonly IDataModelService _dataModelService;
16+
17+
internal AmbientTransaction? AmbientTransaction { get; private set; }
18+
19+
public AmbientTransactionFactory(IJsonApiOptions options, IDataModelService dataModelService)
20+
{
21+
ArgumentGuard.NotNull(options);
22+
ArgumentGuard.NotNull(dataModelService);
23+
24+
_options = options;
25+
_dataModelService = dataModelService;
26+
}
27+
28+
internal async Task<AmbientTransaction> BeginTransactionAsync(CancellationToken cancellationToken)
29+
{
30+
var instance = (IOperationsTransactionFactory)this;
31+
32+
IOperationsTransaction transaction = await instance.BeginTransactionAsync(cancellationToken);
33+
return (AmbientTransaction)transaction;
34+
}
35+
36+
async Task<IOperationsTransaction> IOperationsTransactionFactory.BeginTransactionAsync(CancellationToken cancellationToken)
37+
{
38+
if (AmbientTransaction != null)
39+
{
40+
throw new InvalidOperationException("Cannot start transaction because another transaction is already active.");
41+
}
42+
43+
DbConnection dbConnection = _dataModelService.CreateConnection();
44+
45+
try
46+
{
47+
await dbConnection.OpenAsync(cancellationToken);
48+
49+
DbTransaction transaction = _options.TransactionIsolationLevel != null
50+
? await dbConnection.BeginTransactionAsync(_options.TransactionIsolationLevel.Value, cancellationToken)
51+
: await dbConnection.BeginTransactionAsync(cancellationToken);
52+
53+
var transactionId = Guid.NewGuid();
54+
AmbientTransaction = new AmbientTransaction(this, transaction, transactionId);
55+
56+
return AmbientTransaction;
57+
}
58+
catch (DbException)
59+
{
60+
await dbConnection.DisposeAsync();
61+
throw;
62+
}
63+
}
64+
65+
internal void Detach(AmbientTransaction ambientTransaction)
66+
{
67+
ArgumentGuard.NotNull(ambientTransaction);
68+
69+
if (AmbientTransaction != null && AmbientTransaction == ambientTransaction)
70+
{
71+
AmbientTransaction = null;
72+
}
73+
else
74+
{
75+
throw new InvalidOperationException("Failed to detach ambient transaction.");
76+
}
77+
}
78+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using JsonApiDotNetCore.AtomicOperations;
2+
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.Controllers;
4+
using JsonApiDotNetCore.Middleware;
5+
using JsonApiDotNetCore.Resources;
6+
7+
namespace DapperExample.Controllers;
8+
9+
public sealed class OperationsController : JsonApiOperationsController
10+
{
11+
public OperationsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor,
12+
IJsonApiRequest request, ITargetedFields targetedFields)
13+
: base(options, resourceGraph, loggerFactory, processor, request, targetedFields)
14+
{
15+
}
16+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<PropertyGroup>
3+
<TargetFramework>$(TargetFrameworkName)</TargetFramework>
4+
</PropertyGroup>
5+
6+
<ItemGroup>
7+
<ProjectReference Include="..\..\JsonApiDotNetCore\JsonApiDotNetCore.csproj" />
8+
<ProjectReference Include="..\..\JsonApiDotNetCore.SourceGenerators\JsonApiDotNetCore.SourceGenerators.csproj" OutputItemType="Analyzer"
9+
ReferenceOutputAssembly="false" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Dapper" Version="$(DapperVersion)" />
14+
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="$(EntityFrameworkCoreVersion)" />
15+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(EntityFrameworkCoreVersion)" />
16+
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(NpgsqlVersion)" />
17+
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="$(EntityFrameworkCoreVersion)" />
18+
</ItemGroup>
19+
</Project>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using DapperExample.Models;
2+
using JetBrains.Annotations;
3+
using JsonApiDotNetCore;
4+
using Microsoft.EntityFrameworkCore;
5+
using Microsoft.EntityFrameworkCore.Metadata;
6+
7+
// @formatter:wrap_chained_method_calls chop_always
8+
9+
namespace DapperExample.Data;
10+
11+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
12+
public sealed class AppDbContext : DbContext
13+
{
14+
private readonly IConfiguration _configuration;
15+
16+
public DbSet<TodoItem> TodoItems => Set<TodoItem>();
17+
public DbSet<Person> People => Set<Person>();
18+
public DbSet<LoginAccount> LoginAccounts => Set<LoginAccount>();
19+
public DbSet<AccountRecovery> AccountRecoveries => Set<AccountRecovery>();
20+
public DbSet<Tag> Tags => Set<Tag>();
21+
public DbSet<RgbColor> RgbColors => Set<RgbColor>();
22+
23+
public AppDbContext(DbContextOptions<AppDbContext> options, IConfiguration configuration)
24+
: base(options)
25+
{
26+
ArgumentGuard.NotNull(configuration);
27+
28+
_configuration = configuration;
29+
}
30+
31+
protected override void OnModelCreating(ModelBuilder builder)
32+
{
33+
builder.Entity<Person>()
34+
.HasMany(person => person.AssignedTodoItems)
35+
.WithOne(todoItem => todoItem.Assignee);
36+
37+
builder.Entity<Person>()
38+
.HasMany(person => person.OwnedTodoItems)
39+
.WithOne(todoItem => todoItem.Owner);
40+
41+
builder.Entity<Person>()
42+
.HasOne(person => person.Account)
43+
.WithOne(loginAccount => loginAccount.Person)
44+
.HasForeignKey<Person>("AccountId");
45+
46+
builder.Entity<LoginAccount>()
47+
.HasOne(loginAccount => loginAccount.Recovery)
48+
.WithOne(accountRecovery => accountRecovery.Account)
49+
.HasForeignKey<LoginAccount>("RecoveryId");
50+
51+
builder.Entity<Tag>()
52+
.HasOne(tag => tag.Color)
53+
.WithOne(rgbColor => rgbColor.Tag)
54+
.HasForeignKey<RgbColor>("TagId");
55+
56+
var databaseProvider = _configuration.GetValue<DatabaseProvider>("DatabaseProvider");
57+
58+
if (databaseProvider != DatabaseProvider.SqlServer)
59+
{
60+
// In this example project, all cascades happen in the database, but SQL Server doesn't support that very well.
61+
AdjustDeleteBehaviorForJsonApi(builder);
62+
}
63+
}
64+
65+
private static void AdjustDeleteBehaviorForJsonApi(ModelBuilder builder)
66+
{
67+
foreach (IMutableForeignKey foreignKey in builder.Model.GetEntityTypes()
68+
.SelectMany(entityType => entityType.GetForeignKeys()))
69+
{
70+
if (foreignKey.DeleteBehavior == DeleteBehavior.ClientSetNull)
71+
{
72+
foreignKey.DeleteBehavior = DeleteBehavior.SetNull;
73+
}
74+
75+
if (foreignKey.DeleteBehavior == DeleteBehavior.ClientCascade)
76+
{
77+
foreignKey.DeleteBehavior = DeleteBehavior.Cascade;
78+
}
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)