Skip to content

Commit 455983f

Browse files
authored
Merge pull request #539 from json-api-dotnet/feat/diffablehashset
EntityDiff replaced by IDiffableEntityHashSet
2 parents a82a241 + 8648fe5 commit 455983f

File tree

8 files changed

+41
-55
lines changed

8 files changed

+41
-55
lines changed

src/JsonApiDotNetCore/Hooks/Execution/EntityDiffs.cs renamed to src/JsonApiDotNetCore/Hooks/Execution/DiffableEntityHashSet.cs

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4-
using System.Collections.ObjectModel;
54
using System.Linq;
65
using JsonApiDotNetCore.Internal;
76
using JsonApiDotNetCore.Models;
@@ -15,68 +14,55 @@ namespace JsonApiDotNetCore.Hooks
1514
/// Also contains information about updated relationships through
1615
/// implementation of IRelationshipsDictionary<typeparamref name="TResource"/>>
1716
/// </summary>
18-
public interface IEntityDiffs<TResource> : IEnumerable<EntityDiffPair<TResource>> where TResource : class, IIdentifiable
17+
public interface IDiffableEntityHashSet<TResource> : IEntityHashSet<TResource> where TResource : class, IIdentifiable
1918
{
2019
/// <summary>
21-
/// The database values of the resources affected by the request.
20+
/// Iterates over diffs, which is the affected entity from the request
21+
/// with their associated current value from the database.
2222
/// </summary>
23-
HashSet<TResource> DatabaseValues { get; }
24-
25-
/// <summary>
26-
/// The resources that were affected by the request.
27-
/// </summary>
28-
EntityHashSet<TResource> Entities { get; }
29-
23+
IEnumerable<EntityDiffPair<TResource>> GetDiffs();
24+
3025
}
3126

3227
/// <inheritdoc />
33-
public class EntityDiffs<TResource> : IEntityDiffs<TResource> where TResource : class, IIdentifiable
28+
public class DiffableEntityHashSet<TResource> : EntityHashSet<TResource>, IDiffableEntityHashSet<TResource> where TResource : class, IIdentifiable
3429
{
35-
/// <inheritdoc />
36-
public HashSet<TResource> DatabaseValues { get => _databaseValues ?? ThrowNoDbValuesError(); }
37-
/// <inheritdoc />
38-
public EntityHashSet<TResource> Entities { get; private set; }
39-
4030
private readonly HashSet<TResource> _databaseValues;
4131
private readonly bool _databaseValuesLoaded;
4232

43-
public EntityDiffs(HashSet<TResource> requestEntities,
33+
public DiffableEntityHashSet(HashSet<TResource> requestEntities,
4434
HashSet<TResource> databaseEntities,
45-
Dictionary<RelationshipAttribute, HashSet<TResource>> relationships)
35+
Dictionary<RelationshipAttribute, HashSet<TResource>> relationships)
36+
: base(requestEntities, relationships)
4637
{
47-
Entities = new EntityHashSet<TResource>(requestEntities, relationships);
4838
_databaseValues = databaseEntities;
4939
_databaseValuesLoaded |= _databaseValues != null;
5040
}
5141

5242
/// <summary>
5343
/// Used internally by the ResourceHookExecutor to make live a bit easier with generics
5444
/// </summary>
55-
internal EntityDiffs(IEnumerable requestEntities,
45+
internal DiffableEntityHashSet(IEnumerable requestEntities,
5646
IEnumerable databaseEntities,
5747
Dictionary<RelationshipAttribute, IEnumerable> relationships)
5848
: this((HashSet<TResource>)requestEntities, (HashSet<TResource>)databaseEntities, TypeHelper.ConvertRelationshipDictionary<TResource>(relationships)) { }
5949

6050

6151
/// <inheritdoc />
62-
public IEnumerator<EntityDiffPair<TResource>> GetEnumerator()
52+
public IEnumerable<EntityDiffPair<TResource>> GetDiffs()
6353
{
6454
if (!_databaseValuesLoaded) ThrowNoDbValuesError();
6555

66-
foreach (var entity in Entities)
56+
foreach (var entity in this)
6757
{
68-
TResource currentValueInDatabase = null;
69-
currentValueInDatabase = _databaseValues.Single(e => entity.StringId == e.StringId);
58+
TResource currentValueInDatabase = _databaseValues.Single(e => entity.StringId == e.StringId);
7059
yield return new EntityDiffPair<TResource>(entity, currentValueInDatabase);
7160
}
7261
}
7362

74-
/// <inheritdoc />
75-
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
76-
7763
private HashSet<TResource> ThrowNoDbValuesError()
7864
{
79-
throw new MemberAccessException("Cannot access database entities if the LoadDatabaseValues option is set to false");
65+
throw new MemberAccessException("Cannot iterate over the diffs if the LoadDatabaseValues option is set to false");
8066
}
8167
}
8268

src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ public interface IBeforeHooks<TResource> where TResource : class, IIdentifiable
5454
/// layer just before updating entities of type <typeparamref name="TResource"/>.
5555
/// <para />
5656
/// For the <see cref="ResourcePipeline.Patch"/> pipeline, the
57-
/// <paramref name="entityDiff" /> will typically contain one entity.
57+
/// <paramref name="entities" /> will typically contain one entity.
5858
/// For <see cref="ResourcePipeline.BulkPatch"/>, this it may contain
5959
/// multiple entities.
6060
/// <para />
6161
/// The returned <see cref="IEnumerable{TEntity}"/> may be a subset
62-
/// of the <see cref="EntityDiffs{TEntity}.RequestEntities"/> property in parameter <paramref name="entityDiff"/>,
62+
/// of the <see cref="DiffableEntityHashSet{TEntity}"/> property in parameter <paramref name="entities"/>,
6363
/// in which case the operation of the pipeline will not be executed
6464
/// for the omitted entities. The returned set may also contain custom
6565
/// changes of the properties on the entities.
@@ -75,9 +75,9 @@ public interface IBeforeHooks<TResource> where TResource : class, IIdentifiable
7575
/// hook is fired for these.
7676
/// </summary>
7777
/// <returns>The transformed entity set</returns>
78-
/// <param name="entityDiff">The entity diff.</param>
78+
/// <param name="entities">The affected entities.</param>
7979
/// <param name="pipeline">An enum indicating from where the hook was triggered.</param>
80-
IEnumerable<TResource> BeforeUpdate(IEntityDiffs<TResource> entityDiff, ResourcePipeline pipeline);
80+
IEnumerable<TResource> BeforeUpdate(IDiffableEntityHashSet<TResource> entities, ResourcePipeline pipeline);
8181

8282
/// <summary>
8383
/// Implement this hook to run custom logic in the <see cref=" EntityResourceService{T}"/>

src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public virtual IEnumerable<TEntity> BeforeUpdate<TEntity>(IEnumerable<TEntity> e
4949
{
5050
var relationships = node.RelationshipsToNextLayer.Select(p => p.Attribute).ToArray();
5151
var dbValues = LoadDbValues(typeof(TEntity), (IEnumerable<TEntity>)node.UniqueEntities, ResourceHook.BeforeUpdate, relationships);
52-
var diff = new EntityDiffs<TEntity>(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer());
52+
var diff = new DiffableEntityHashSet<TEntity>(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer());
5353
IEnumerable<TEntity> updated = container.BeforeUpdate(diff, pipeline);
5454
node.UpdateUnique(updated);
5555
node.Reassign(entities);

src/JsonApiDotNetCore/Models/ResourceDefinition.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public virtual void AfterUpdateRelationship(IRelationshipsDictionary<T> entities
179179
/// <inheritdoc/>
180180
public virtual void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { }
181181
/// <inheritdoc/>
182-
public virtual IEnumerable<T> BeforeUpdate(IEntityDiffs<T> entityDiff, ResourcePipeline pipeline) { return entityDiff.Entities; }
182+
public virtual IEnumerable<T> BeforeUpdate(IDiffableEntityHashSet<T> entities, ResourcePipeline pipeline) { return entities; }
183183
/// <inheritdoc/>
184184
public virtual IEnumerable<T> BeforeDelete(IEntityHashSet<T> entities, ResourcePipeline pipeline) { return entities; }
185185
/// <inheritdoc/>

test/UnitTests/ResourceHooks/AffectedEntitiesHelperTests.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ public void EntityDiff_GetByRelationships()
8787
{
8888
// arrange
8989
var dbEntities = new HashSet<Dummy>(AllEntities.Select(e => new Dummy { Id = e.Id }).ToList());
90-
EntityDiffs<Dummy> diffs = new EntityDiffs<Dummy>(AllEntities, dbEntities, Relationships);
90+
DiffableEntityHashSet<Dummy> diffs = new DiffableEntityHashSet<Dummy>(AllEntities, dbEntities, Relationships);
9191

9292
// act
93-
Dictionary<RelationshipAttribute, HashSet<Dummy>> toOnes = diffs.Entities.GetByRelationship<ToOne>();
94-
Dictionary<RelationshipAttribute, HashSet<Dummy>> toManies = diffs.Entities.GetByRelationship<ToMany>();
95-
Dictionary<RelationshipAttribute, HashSet<Dummy>> notTargeted = diffs.Entities.GetByRelationship<NotTargeted>();
96-
Dictionary<RelationshipAttribute, HashSet<Dummy>> allRelationships = diffs.Entities.AffectedRelationships;
93+
Dictionary<RelationshipAttribute, HashSet<Dummy>> toOnes = diffs.GetByRelationship<ToOne>();
94+
Dictionary<RelationshipAttribute, HashSet<Dummy>> toManies = diffs.GetByRelationship<ToMany>();
95+
Dictionary<RelationshipAttribute, HashSet<Dummy>> notTargeted = diffs.GetByRelationship<NotTargeted>();
96+
Dictionary<RelationshipAttribute, HashSet<Dummy>> allRelationships = diffs.AffectedRelationships;
9797

9898
// Assert
9999
AssertRelationshipDictionaryGetters(allRelationships, toOnes, toManies, notTargeted);
@@ -103,12 +103,12 @@ public void EntityDiff_GetByRelationships()
103103
Assert.DoesNotContain(e, allEntitiesWithAffectedRelationships);
104104
});
105105

106-
var requestEntitiesFromDiff = diffs.Entities;
106+
var requestEntitiesFromDiff = diffs;
107107
requestEntitiesFromDiff.ToList().ForEach(e =>
108108
{
109109
Assert.Contains(e, AllEntities);
110110
});
111-
var databaseEntitiesFromDiff = diffs.DatabaseValues;
111+
var databaseEntitiesFromDiff = diffs.GetDiffs().Select( d => d.DatabaseValue);
112112
databaseEntitiesFromDiff.ToList().ForEach(e =>
113113
{
114114
Assert.Contains(e, dbEntities);
@@ -120,10 +120,10 @@ public void EntityDiff_Loops_Over_Diffs()
120120
{
121121
// arrange
122122
var dbEntities = new HashSet<Dummy>(AllEntities.Select(e => new Dummy { Id = e.Id }));
123-
EntityDiffs<Dummy> diffs = new EntityDiffs<Dummy>(AllEntities, dbEntities, Relationships);
123+
DiffableEntityHashSet<Dummy> diffs = new DiffableEntityHashSet<Dummy>(AllEntities, dbEntities, Relationships);
124124

125125
// Assert & act
126-
foreach (EntityDiffPair<Dummy> diff in diffs)
126+
foreach (EntityDiffPair<Dummy> diff in diffs.GetDiffs())
127127
{
128128
Assert.Equal(diff.Entity.Id, diff.DatabaseValue.Id);
129129
Assert.NotEqual(diff.Entity, diff.DatabaseValue);

test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public void BeforeUpdate()
2424
hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch);
2525

2626
// assert
27-
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny<IEntityDiffs<TodoItem>>(), ResourcePipeline.Patch), Times.Once());
27+
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny<IDiffableEntityHashSet<TodoItem>>(), ResourcePipeline.Patch), Times.Once());
2828
ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny<HashSet<string>>(), It.IsAny<IRelationshipsDictionary<Person>>(), ResourcePipeline.Patch), Times.Once());
2929
VerifyNoOtherCalls(todoResourceMock, ownerResourceMock);
3030
}
@@ -62,7 +62,7 @@ public void BeforeUpdate_Without_Child_Hook_Implemented()
6262
hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch);
6363

6464
// assert
65-
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny<IEntityDiffs<TodoItem>>(), ResourcePipeline.Patch), Times.Once());
65+
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny<IDiffableEntityHashSet<TodoItem>>(), ResourcePipeline.Patch), Times.Once());
6666
VerifyNoOtherCalls(todoResourceMock, ownerResourceMock);
6767
}
6868

test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public void BeforeUpdate()
5959
hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch);
6060

6161
// assert
62-
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IEntityDiffs<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
62+
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IDiffableEntityHashSet<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
6363
ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(
6464
It.Is<HashSet<string>>(ids => PersonIdCheck(ids, personId)),
6565
It.Is<IRelationshipsDictionary<Person>>(rh => PersonCheck(lastName, rh)),
@@ -93,7 +93,7 @@ public void BeforeUpdate_Deleting_Relationship()
9393
hookExecutor.BeforeUpdate(_todoList, ResourcePipeline.Patch);
9494

9595
// assert
96-
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IEntityDiffs<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
96+
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IDiffableEntityHashSet<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
9797
ownerResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(
9898
It.Is<IRelationshipsDictionary<Person>>(rh => PersonCheck(lastName + lastName, rh)),
9999
ResourcePipeline.Patch),
@@ -140,7 +140,7 @@ public void BeforeUpdate_Without_Child_Hook_Implemented()
140140
hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch);
141141

142142
// assert
143-
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IEntityDiffs<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
143+
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IDiffableEntityHashSet<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
144144
todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(
145145
It.Is<IRelationshipsDictionary<TodoItem>>(rh => TodoCheck(rh, description + description)),
146146
ResourcePipeline.Patch),
@@ -161,7 +161,7 @@ public void BeforeUpdate_NoImplicit()
161161
hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch);
162162

163163
// assert
164-
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IEntityDiffs<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
164+
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IDiffableEntityHashSet<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
165165
ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(
166166
It.Is<HashSet<string>>(ids => PersonIdCheck(ids, personId)),
167167
It.IsAny<IRelationshipsDictionary<Person>>(),
@@ -204,18 +204,18 @@ public void BeforeUpdate_NoImplicit_Without_Child_Hook_Implemented()
204204
hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch);
205205

206206
// assert
207-
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IEntityDiffs<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
207+
todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is<IDiffableEntityHashSet<TodoItem>>((diff) => TodoCheckDiff(diff, description)), ResourcePipeline.Patch), Times.Once());
208208
VerifyNoOtherCalls(todoResourceMock, ownerResourceMock);
209209
}
210210

211-
private bool TodoCheckDiff(IEntityDiffs<TodoItem> diff, string checksum)
211+
private bool TodoCheckDiff(IDiffableEntityHashSet<TodoItem> entities, string checksum)
212212
{
213-
var diffPair = diff.Single();
213+
var diffPair = entities.GetDiffs().Single();
214214
var dbCheck = diffPair.DatabaseValue.Description == checksum;
215215
var reqCheck = diffPair.Entity.Description == null;
216216
var diffPairCheck = (dbCheck && reqCheck);
217217

218-
var updatedRelationship = diff.Entities.GetByRelationship<Person>().Single();
218+
var updatedRelationship = entities.GetByRelationship<Person>().Single();
219219
var diffcheck = updatedRelationship.Key.PublicRelationshipName == "one-to-one-person";
220220

221221
return (dbCheck && reqCheck && diffcheck);

test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,8 @@ void MockHooks<TModel>(Mock<IResourceHookContainer<TModel>> resourceDefinition)
260260
.Setup(rd => rd.BeforeRead(It.IsAny<ResourcePipeline>(), It.IsAny<bool>(), It.IsAny<string>()))
261261
.Verifiable();
262262
resourceDefinition
263-
.Setup(rd => rd.BeforeUpdate(It.IsAny<IEntityDiffs<TModel>>(), It.IsAny<ResourcePipeline>()))
264-
.Returns<EntityDiffs<TModel>, ResourcePipeline>((entityDiff, context) => entityDiff.Entities)
263+
.Setup(rd => rd.BeforeUpdate(It.IsAny<IDiffableEntityHashSet<TModel>>(), It.IsAny<ResourcePipeline>()))
264+
.Returns<DiffableEntityHashSet<TModel>, ResourcePipeline>((entities, context) => entities)
265265
.Verifiable();
266266
resourceDefinition
267267
.Setup(rd => rd.BeforeDelete(It.IsAny<IEntityHashSet<TModel>>(), It.IsAny<ResourcePipeline>()))

0 commit comments

Comments
 (0)