Skip to content

Commit a82a241

Browse files
authored
Merge pull request #533 from json-api-dotnet/fix/hooks-hotfix
Hotfixes related to Resource Hooks
2 parents 8cca15a + 99c8e10 commit a82a241

19 files changed

+466
-200
lines changed

src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs

+2-6
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
5858
.WithOne(t => t.ParentTodoItem)
5959
.HasForeignKey(t => t.ParentTodoItemId);
6060

61-
modelBuilder.Entity<Person>()
62-
.HasOne(p => p.Passport)
63-
.WithOne(p => p.Person)
64-
.HasForeignKey<Person>(p => p.PassportId);
65-
6661
modelBuilder.Entity<Passport>()
6762
.HasOne(p => p.Person)
6863
.WithOne(p => p.Passport)
69-
.HasForeignKey<Person>(p => p.PassportId);
64+
.HasForeignKey<Person>(p => p.PassportId)
65+
.OnDelete(DeleteBehavior.SetNull);
7066

7167
modelBuilder.Entity<TodoItem>()
7268
.HasOne(p => p.ToOnePerson)

src/Examples/JsonApiDotNetCoreExample/Resources/LockableResourceBase.cs renamed to src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
namespace JsonApiDotNetCoreExample.Resources
99
{
10-
public abstract class LockableResourceBase<T> : ResourceDefinition<T> where T : class, IIsLockable, IIdentifiable
10+
public abstract class LockableResource<T> : ResourceDefinition<T> where T : class, IIsLockable, IIdentifiable
1111
{
12-
protected LockableResourceBase(IResourceGraph graph) : base(graph) { }
12+
protected LockableResource(IResourceGraph graph) : base(graph) { }
1313

1414
protected void DisallowLocked(IEnumerable<T> entities)
1515
{

src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs

+5-11
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,19 @@
66

77
namespace JsonApiDotNetCoreExample.Resources
88
{
9-
public class PersonResource : LockableResourceBase<Person>
9+
public class PersonResource : LockableResource<Person>
1010
{
1111
public PersonResource(IResourceGraph graph) : base(graph) { }
1212

13-
public override IEnumerable<string> BeforeUpdateRelationship(HashSet<string> ids, IRelationshipsDictionary<Person> resourcesByRelationship, ResourcePipeline pipeline)
13+
public override IEnumerable<string> BeforeUpdateRelationship(HashSet<string> ids, IRelationshipsDictionary<Person> entitiesByRelationship, ResourcePipeline pipeline)
1414
{
15-
BeforeImplicitUpdateRelationship(resourcesByRelationship, pipeline);
15+
BeforeImplicitUpdateRelationship(entitiesByRelationship, pipeline);
1616
return ids;
1717
}
1818

19-
//[LoadDatabaseValues(true)]
20-
//public override IEnumerable<Person> BeforeUpdate(IResourceDiff<Person> entityDiff, ResourcePipeline pipeline)
21-
//{
22-
// return entityDiff.Entities;
23-
//}
24-
25-
public override void BeforeImplicitUpdateRelationship(IRelationshipsDictionary<Person> resourcesByRelationship, ResourcePipeline pipeline)
19+
public override void BeforeImplicitUpdateRelationship(IRelationshipsDictionary<Person> entitiesByRelationship, ResourcePipeline pipeline)
2620
{
27-
resourcesByRelationship.GetByRelationship<Passport>().ToList().ForEach(kvp => DisallowLocked(kvp.Value));
21+
entitiesByRelationship.GetByRelationship<Passport>().ToList().ForEach(kvp => DisallowLocked(kvp.Value));
2822
}
2923
}
3024
}

src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace JsonApiDotNetCoreExample.Resources
99
{
10-
public class TodoResource : LockableResourceBase<TodoItem>
10+
public class TodoResource : LockableResource<TodoItem>
1111
{
1212
public TodoResource(IResourceGraph graph) : base(graph) { }
1313

Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4+
using System.Collections.ObjectModel;
45
using System.Linq;
56
using JsonApiDotNetCore.Internal;
67
using JsonApiDotNetCore.Models;
@@ -12,40 +13,38 @@ namespace JsonApiDotNetCore.Hooks
1213
/// Contains the resources from the request and the corresponding database values.
1314
///
1415
/// Also contains information about updated relationships through
15-
/// implementation of IRelationshipsDictionary<typeparamref name="TEntity"/>>
16+
/// implementation of IRelationshipsDictionary<typeparamref name="TResource"/>>
1617
/// </summary>
17-
public interface IEntityDiff<TEntity> : IRelationshipsDictionary<TEntity>, IEnumerable<EntityDiffPair<TEntity>> where TEntity : class, IIdentifiable
18+
public interface IEntityDiffs<TResource> : IEnumerable<EntityDiffPair<TResource>> where TResource : class, IIdentifiable
1819
{
1920
/// <summary>
2021
/// The database values of the resources affected by the request.
2122
/// </summary>
22-
HashSet<TEntity> DatabaseValues { get; }
23+
HashSet<TResource> DatabaseValues { get; }
2324

2425
/// <summary>
2526
/// The resources that were affected by the request.
2627
/// </summary>
27-
HashSet<TEntity> Entities { get; }
28+
EntityHashSet<TResource> Entities { get; }
29+
2830
}
2931

3032
/// <inheritdoc />
31-
public class EntityDiffs<TEntity> : IEntityDiff<TEntity> where TEntity : class, IIdentifiable
33+
public class EntityDiffs<TResource> : IEntityDiffs<TResource> where TResource : class, IIdentifiable
3234
{
3335
/// <inheritdoc />
34-
public HashSet<TEntity> DatabaseValues { get => _databaseValues ?? ThrowNoDbValuesError(); }
35-
private readonly HashSet<TEntity> _databaseValues;
36-
private readonly bool _databaseValuesLoaded;
37-
38-
/// <inheritdoc />
39-
public HashSet<TEntity> Entities { get; private set; }
36+
public HashSet<TResource> DatabaseValues { get => _databaseValues ?? ThrowNoDbValuesError(); }
4037
/// <inheritdoc />
41-
public RelationshipsDictionary<TEntity> AffectedRelationships { get; private set; }
38+
public EntityHashSet<TResource> Entities { get; private set; }
4239

43-
public EntityDiffs(HashSet<TEntity> requestEntities,
44-
HashSet<TEntity> databaseEntities,
45-
Dictionary<RelationshipAttribute, HashSet<TEntity>> relationships)
40+
private readonly HashSet<TResource> _databaseValues;
41+
private readonly bool _databaseValuesLoaded;
42+
43+
public EntityDiffs(HashSet<TResource> requestEntities,
44+
HashSet<TResource> databaseEntities,
45+
Dictionary<RelationshipAttribute, HashSet<TResource>> relationships)
4646
{
47-
Entities = requestEntities;
48-
AffectedRelationships = new RelationshipsDictionary<TEntity>(relationships);
47+
Entities = new EntityHashSet<TResource>(requestEntities, relationships);
4948
_databaseValues = databaseEntities;
5049
_databaseValuesLoaded |= _databaseValues != null;
5150
}
@@ -55,39 +54,27 @@ public EntityDiffs(HashSet<TEntity> requestEntities,
5554
/// </summary>
5655
internal EntityDiffs(IEnumerable requestEntities,
5756
IEnumerable databaseEntities,
58-
Dictionary<RelationshipAttribute, IEnumerable> relationships)
59-
: this((HashSet<TEntity>)requestEntities, (HashSet<TEntity>)databaseEntities, TypeHelper.ConvertRelationshipDictionary<TEntity>(relationships)) { }
60-
57+
Dictionary<RelationshipAttribute, IEnumerable> relationships)
58+
: this((HashSet<TResource>)requestEntities, (HashSet<TResource>)databaseEntities, TypeHelper.ConvertRelationshipDictionary<TResource>(relationships)) { }
6159

62-
/// <inheritdoc />
63-
public Dictionary<RelationshipAttribute, HashSet<TEntity>> GetByRelationship<TPrincipalResource>() where TPrincipalResource : class, IIdentifiable
64-
{
65-
return GetByRelationship(typeof(TPrincipalResource));
66-
}
67-
68-
/// <inheritdoc />
69-
public Dictionary<RelationshipAttribute, HashSet<TEntity>> GetByRelationship(Type principalType)
70-
{
71-
return AffectedRelationships.GetByRelationship(principalType);
72-
}
7360

7461
/// <inheritdoc />
75-
public IEnumerator<EntityDiffPair<TEntity>> GetEnumerator()
62+
public IEnumerator<EntityDiffPair<TResource>> GetEnumerator()
7663
{
7764
if (!_databaseValuesLoaded) ThrowNoDbValuesError();
7865

7966
foreach (var entity in Entities)
8067
{
81-
TEntity currentValueInDatabase = null;
68+
TResource currentValueInDatabase = null;
8269
currentValueInDatabase = _databaseValues.Single(e => entity.StringId == e.StringId);
83-
yield return new EntityDiffPair<TEntity>(entity, currentValueInDatabase);
70+
yield return new EntityDiffPair<TResource>(entity, currentValueInDatabase);
8471
}
8572
}
8673

8774
/// <inheritdoc />
8875
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
8976

90-
private HashSet<TEntity> ThrowNoDbValuesError()
77+
private HashSet<TResource> ThrowNoDbValuesError()
9178
{
9279
throw new MemberAccessException("Cannot access database entities if the LoadDatabaseValues option is set to false");
9380
}
@@ -97,9 +84,9 @@ private HashSet<TEntity> ThrowNoDbValuesError()
9784
/// A wrapper that contains an entity that is affected by the request,
9885
/// matched to its current database value
9986
/// </summary>
100-
public class EntityDiffPair<TEntity> where TEntity : class, IIdentifiable
87+
public class EntityDiffPair<TResource> where TResource : class, IIdentifiable
10188
{
102-
public EntityDiffPair(TEntity entity, TEntity databaseValue)
89+
public EntityDiffPair(TResource entity, TResource databaseValue)
10390
{
10491
Entity = entity;
10592
DatabaseValue = databaseValue;
@@ -108,10 +95,10 @@ public EntityDiffPair(TEntity entity, TEntity databaseValue)
10895
/// <summary>
10996
/// The resource from the request matching the resource from the database.
11097
/// </summary>
111-
public TEntity Entity { get; private set; }
98+
public TResource Entity { get; private set; }
11299
/// <summary>
113100
/// The resource from the database matching the resource from the request.
114101
/// </summary>
115-
public TEntity DatabaseValue { get; private set; }
102+
public TResource DatabaseValue { get; private set; }
116103
}
117104
}

src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs

+11-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System.Collections;
44
using JsonApiDotNetCore.Internal;
55
using System;
6+
using System.Collections.ObjectModel;
7+
using System.Collections.Immutable;
68

79
namespace JsonApiDotNetCore.Hooks
810
{
@@ -12,7 +14,7 @@ namespace JsonApiDotNetCore.Hooks
1214
/// Also contains information about updated relationships through
1315
/// implementation of IAffectedRelationshipsDictionary<typeparamref name="TResource"/>>
1416
/// </summary>
15-
public interface IEntityHashSet<TResource> : IRelationshipsDictionary<TResource>, IEnumerable<TResource> where TResource : class, IIdentifiable { }
17+
public interface IEntityHashSet<TResource> : IByAffectedRelationships<TResource>, IReadOnlyCollection<TResource> where TResource : class, IIdentifiable { }
1618

1719
/// <summary>
1820
/// Implementation of IResourceHashSet{TResource}.
@@ -24,13 +26,16 @@ public interface IEntityHashSet<TResource> : IRelationshipsDictionary<TResource>
2426
/// </summary>
2527
public class EntityHashSet<TResource> : HashSet<TResource>, IEntityHashSet<TResource> where TResource : class, IIdentifiable
2628
{
29+
30+
2731
/// <inheritdoc />
28-
public RelationshipsDictionary<TResource> AffectedRelationships { get; private set; }
32+
public Dictionary<RelationshipAttribute, HashSet<TResource>> AffectedRelationships { get => _relationships; }
33+
private readonly RelationshipsDictionary<TResource> _relationships;
2934

3035
public EntityHashSet(HashSet<TResource> entities,
3136
Dictionary<RelationshipAttribute, HashSet<TResource>> relationships) : base(entities)
3237
{
33-
AffectedRelationships = new RelationshipsDictionary<TResource>(relationships);
38+
_relationships = new RelationshipsDictionary<TResource>(relationships);
3439
}
3540

3641
/// <summary>
@@ -44,13 +49,13 @@ internal EntityHashSet(IEnumerable entities,
4449
/// <inheritdoc />
4550
public Dictionary<RelationshipAttribute, HashSet<TResource>> GetByRelationship(Type principalType)
4651
{
47-
return AffectedRelationships.GetByRelationship(principalType);
52+
return _relationships.GetByRelationship(principalType);
4853
}
4954

5055
/// <inheritdoc />
51-
public Dictionary<RelationshipAttribute, HashSet<TResource>> GetByRelationship<TPrincipalResource>() where TPrincipalResource : class, IIdentifiable
56+
public Dictionary<RelationshipAttribute, HashSet<TResource>> GetByRelationship<TRelatedResource>() where TRelatedResource : class, IIdentifiable
5257
{
53-
return GetByRelationship<TPrincipalResource>();
58+
return GetByRelationship(typeof(TRelatedResource));
5459
}
5560
}
5661
}

src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs

+1-6
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public IResourceHookContainer<TEntity> GetResourceHookContainer<TEntity>(Resourc
8282
public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] relationships)
8383
{
8484
var paths = relationships.Select(p => p.RelationshipPath).ToArray();
85-
var idType = GetIdentifierType(entityTypeForRepository);
85+
var idType = TypeHelper.GetIdentifierType(entityTypeForRepository);
8686
var parameterizedGetWhere = GetType()
8787
.GetMethod(nameof(GetWhereAndInclude), BindingFlags.NonPublic | BindingFlags.Instance)
8888
.MakeGenericMethod(entityTypeForRepository, idType);
@@ -144,11 +144,6 @@ IHooksDiscovery GetHookDiscovery(Type entityType)
144144
return discovery;
145145
}
146146

147-
Type GetIdentifierType(Type entityType)
148-
{
149-
return entityType.GetProperty("Id").PropertyType;
150-
}
151-
152147
IEnumerable<TEntity> GetWhereAndInclude<TEntity, TId>(IEnumerable<TId> ids, string[] relationshipPaths) where TEntity : class, IIdentifiable<TId>
153148
{
154149
var repo = GetRepository<TEntity, TId>();

src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs

+14-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,20 @@ internal interface IHookExecutorHelper
4242
/// <summary>
4343
/// For a set of entities, loads current values from the database
4444
/// </summary>
45+
/// <param name="repositoryEntityType">type of the entities to be loaded</param>
46+
/// <param name="entities">The set of entities to load the db values for</param>
47+
/// <param name="hook">The hook in which the db values will be displayed.</param>
48+
/// <param name="relationships">Relationships that need to be included on entities.</param>
4549
IEnumerable LoadDbValues(Type repositoryEntityType, IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] relationships);
46-
bool ShouldLoadDbValues(Type containerEntityType, ResourceHook hook);
50+
51+
/// <summary>
52+
/// Checks if the display database values option is allowed for the targetd hook, and for
53+
/// a given resource of type <paramref name="entityType"/> checks if this hook is implemented and if the
54+
/// database values option is enabled.
55+
/// </summary>
56+
/// <returns><c>true</c>, if load db values was shoulded, <c>false</c> otherwise.</returns>
57+
/// <param name="entityType">Container entity type.</param>
58+
/// <param name="hook">Hook.</param>
59+
bool ShouldLoadDbValues(Type entityType, ResourceHook hook);
4760
}
4861
}

0 commit comments

Comments
 (0)