Skip to content

Commit 9838627

Browse files
committed
chore: cherry picked inverse relationships from hooks branch
1 parent 652d65f commit 9838627

File tree

7 files changed

+87
-6
lines changed

7 files changed

+87
-6
lines changed

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ private IList GetTrackedManyRelationshipValue(IEnumerable<IIdentifiable> relatio
281281
// helper method used in GetTrackedRelationshipValue. See comments there.
282282
private IIdentifiable GetTrackedHasOneRelationshipValue(IIdentifiable relationshipValue, HasOneAttribute hasOneAttribute, ref bool wasAlreadyAttached)
283283
{
284-
285284
var tracked = AttachOrGetTracked(relationshipValue);
286285
if (tracked != null) wasAlreadyAttached = true;
287286
return tracked ?? relationshipValue;

src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using JsonApiDotNetCore.Middleware;
55
using Microsoft.AspNetCore.Builder;
66
using Microsoft.AspNetCore.Hosting;
7+
using Microsoft.Extensions.DependencyInjection;
78
using Microsoft.Extensions.Logging;
89

910
namespace JsonApiDotNetCore.Extensions
@@ -21,6 +22,12 @@ public static IApplicationBuilder UseJsonApi(this IApplicationBuilder app, bool
2122
if (useMvc)
2223
app.UseMvc();
2324

25+
using (var scope = app.ApplicationServices.CreateScope())
26+
{
27+
var inverseRelationshipResolver = scope.ServiceProvider.GetService<IInverseRelationships>();
28+
inverseRelationshipResolver?.Resolve();
29+
}
30+
2431
return app;
2532
}
2633

src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ public static void AddJsonApiInternals(
155155
services.AddScoped<IControllerContext, Services.ControllerContext>();
156156
services.AddScoped<IDocumentBuilderOptionsProvider, DocumentBuilderOptionsProvider>();
157157

158+
services.AddScoped<IInverseRelationships, InverseRelationships>();
159+
160+
158161
// services.AddScoped<IActionFilter, TypeMatchFilter>();
159162
}
160163

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using JsonApiDotNetCore.Data;
3+
using JsonApiDotNetCore.Models;
4+
using Microsoft.EntityFrameworkCore;
5+
using Microsoft.EntityFrameworkCore.Metadata;
6+
7+
namespace JsonApiDotNetCore.Internal
8+
{
9+
/// <summary>
10+
/// Responsible for populating the RelationshipAttribute InverseNavigation property.
11+
///
12+
/// This service is instantiated in the configure phase of the application.
13+
///
14+
/// When using a data access layer different from EF Core, and when using ResourceHooks
15+
/// that depend on the inverse navigation property (BeforeImplicitUpdateRelationship),
16+
/// you will need to override this service, or pass along the inverseNavigationProperty in
17+
/// the RelationshipAttribute.
18+
/// </summary>
19+
public interface IInverseRelationships
20+
{
21+
void Resolve();
22+
}
23+
24+
public class InverseRelationships : IInverseRelationships
25+
{
26+
private readonly ResourceGraph _graph;
27+
private readonly IDbContextResolver _resolver;
28+
29+
public InverseRelationships(IResourceGraph graph, IDbContextResolver resolver = null)
30+
{
31+
_graph = (ResourceGraph)graph;
32+
_resolver = resolver;
33+
}
34+
35+
public void Resolve()
36+
{
37+
if (EntityFrameworkCoreIsEnabled())
38+
{
39+
DbContext context = _resolver.GetContext();
40+
41+
foreach (ContextEntity ce in _graph.Entities)
42+
{
43+
IEntityType meta = context.Model.FindEntityType(ce.EntityType);
44+
if (meta == null) continue;
45+
foreach (var attr in ce.Relationships)
46+
{
47+
if (attr is HasManyThroughAttribute) continue;
48+
INavigation inverseNavigation = meta.FindNavigation(attr.InternalRelationshipName)?.FindInverse();
49+
attr.InverseNavigation = inverseNavigation?.Name;
50+
}
51+
}
52+
}
53+
}
54+
55+
/// <summary>
56+
/// If EF Core is not being used, we're expecting the resolver to not be registered.
57+
/// </summary>
58+
/// <returns><c>true</c>, if entity framework core was enabled, <c>false</c> otherwise.</returns>
59+
/// <param name="resolver">Resolver.</param>
60+
private bool EntityFrameworkCoreIsEnabled()
61+
{
62+
return _resolver != null;
63+
}
64+
}
65+
}

src/JsonApiDotNetCore/Models/HasManyAttribute.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ public class HasManyAttribute : RelationshipAttribute
2424
/// </code>
2525
///
2626
/// </example>
27-
public HasManyAttribute(string publicName = null, Link documentLinks = Link.All, bool canInclude = true, string mappedBy = null)
27+
public HasManyAttribute(string publicName = null, Link documentLinks = Link.All, bool canInclude = true, string mappedBy = null, string inverseNavigationProperty = null)
2828
: base(publicName, documentLinks, canInclude, mappedBy)
29-
{ }
29+
{
30+
InverseNavigation = inverseNavigationProperty;
31+
}
3032

3133
/// <summary>
3234
/// Sets the value of the property identified by this attribute

src/JsonApiDotNetCore/Models/HasOneAttribute.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@ public class HasOneAttribute : RelationshipAttribute
2828
/// </code>
2929
///
3030
/// </example>
31-
public HasOneAttribute(string publicName = null, Link documentLinks = Link.All, bool canInclude = true, string withForeignKey = null, string mappedBy = null)
31+
public HasOneAttribute(string publicName = null, Link documentLinks = Link.All, bool canInclude = true, string withForeignKey = null, string mappedBy = null, string inverseNavigationProperty = null)
3232
: base(publicName, documentLinks, canInclude, mappedBy)
3333
{
3434
_explicitIdentifiablePropertyName = withForeignKey;
35+
InverseNavigation = inverseNavigationProperty;
3536
}
37+
public string InverseNavigation { get; internal set; }
38+
3639

3740
private readonly string _explicitIdentifiablePropertyName;
3841

src/JsonApiDotNetCore/Models/RelationshipAttribute.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ protected RelationshipAttribute(string publicName, Link documentLinks, bool canI
1515
}
1616

1717
public string PublicRelationshipName { get; internal set; }
18-
public string InternalRelationshipName { get; internal set; }
19-
18+
public string InternalRelationshipName { get; internal set; }
19+
20+
public string InverseNavigation { get; internal set; }
21+
2022
/// <summary>
2123
/// The related entity type. This does not necessarily match the navigation property type.
2224
/// In the case of a HasMany relationship, this value will be the generic argument type.

0 commit comments

Comments
 (0)