Skip to content

Commit 46a3c02

Browse files
committed
fix: #492
1 parent 8ca7934 commit 46a3c02

File tree

3 files changed

+41
-21
lines changed

3 files changed

+41
-21
lines changed

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ public void DetachRelationshipPointers(TEntity entity)
163163
{
164164
foreach (var hasOneRelationship in _jsonApiContext.HasOneRelationshipPointers.Get())
165165
{
166-
var hasOne = (HasOneAttribute) hasOneRelationship.Key;
166+
var hasOne = (HasOneAttribute)hasOneRelationship.Key;
167167
if (hasOne.EntityPropertyName != null)
168168
{
169169
var relatedEntity = entity.GetType().GetProperty(hasOne.EntityPropertyName)?.GetValue(entity);
@@ -178,7 +178,7 @@ public void DetachRelationshipPointers(TEntity entity)
178178

179179
foreach (var hasManyRelationship in _jsonApiContext.HasManyRelationshipPointers.Get())
180180
{
181-
var hasMany = (HasManyAttribute) hasManyRelationship.Key;
181+
var hasMany = (HasManyAttribute)hasManyRelationship.Key;
182182
if (hasMany.EntityPropertyName != null)
183183
{
184184
var relatedList = (IList)entity.GetType().GetProperty(hasMany.EntityPropertyName)?.GetValue(entity);
@@ -194,7 +194,7 @@ public void DetachRelationshipPointers(TEntity entity)
194194
_context.Entry(pointer).State = EntityState.Detached;
195195
}
196196
}
197-
197+
198198
// HACK: detaching has many relationships doesn't appear to be sufficient
199199
// the navigation property actually needs to be nulled out, otherwise
200200
// EF adds duplicate instances to the collection
@@ -234,7 +234,7 @@ private void AttachHasMany(TEntity entity, HasManyAttribute relationship, IList
234234
{
235235
_context.Entry(pointer).State = EntityState.Unchanged;
236236
}
237-
}
237+
}
238238
}
239239

240240
private void AttachHasManyThrough(TEntity entity, HasManyThroughAttribute hasManyThrough, IList pointers)
@@ -270,7 +270,7 @@ private void AttachHasOnePointers(TEntity entity)
270270
if (relationship.Key.GetType() != typeof(HasOneAttribute))
271271
continue;
272272

273-
var hasOne = (HasOneAttribute) relationship.Key;
273+
var hasOne = (HasOneAttribute)relationship.Key;
274274
if (hasOne.EntityPropertyName != null)
275275
{
276276
var relatedEntity = entity.GetType().GetProperty(hasOne.EntityPropertyName)?.GetValue(entity);
@@ -296,13 +296,19 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity entity)
296296
foreach (var attr in _jsonApiContext.AttributesToUpdate)
297297
attr.Key.SetValue(oldEntity, attr.Value);
298298

299-
foreach (var relationship in _jsonApiContext.RelationshipsToUpdate)
300-
relationship.Key.SetValue(oldEntity, relationship.Value);
301-
302-
AttachRelationships(oldEntity);
303-
299+
if (_jsonApiContext.RelationshipsToUpdate.Any())
300+
{
301+
AttachRelationships(oldEntity);
302+
foreach (var relationship in _jsonApiContext.RelationshipsToUpdate)
303+
{
304+
/// If we are updating to-many relations from PATCH, we need to include the relation first,
305+
/// else it will not peform a complete replacement, as required by the specs.
306+
if (relationship.Key.IsHasMany)
307+
await _context.Entry(oldEntity).Collection(relationship.Key.InternalRelationshipName).LoadAsync();
308+
relationship.Key.SetValue(oldEntity, relationship.Value); // article.tags = nieuwe lijst
309+
}
310+
}
304311
await _context.SaveChangesAsync();
305-
306312
return oldEntity;
307313
}
308314

@@ -366,7 +372,7 @@ public virtual IQueryable<TEntity> Include(IQueryable<TEntity> entities, string
366372
? relationship.RelationshipPath
367373
: $"{internalRelationshipPath}.{relationship.RelationshipPath}";
368374

369-
if(i < relationshipChain.Length)
375+
if (i < relationshipChain.Length)
370376
entity = _jsonApiContext.ResourceGraph.GetContextEntity(relationship.Type);
371377
}
372378

src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ private object SetRelationships(
186186
{
187187
entity = attr.IsHasOne
188188
? SetHasOneRelationship(entity, entityProperties, (HasOneAttribute)attr, contextEntity, relationships, included)
189-
: SetHasManyRelationship(entity, entityProperties, attr, contextEntity, relationships, included);
189+
: SetHasManyRelationship(entity, entityProperties, (HasManyAttribute)attr, contextEntity, relationships, included);
190190
}
191191

192192
return entity;
@@ -230,6 +230,15 @@ private object SetHasOneRelationship(object entity,
230230
return entity;
231231
}
232232

233+
/// @TODO investigate the following: when running the Can_Patch_Entity_And_HasOne_Relationship, the updating of the to-one relation happens
234+
/// by assigning a new value to the foreignkey on the TodoItem: todoItem.ownerId = new value.
235+
/// However, this happens in two places: here in this method, but also in the repository, here
236+
/// https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/8f685a6a23515acf8f440c70d20444a0cec1c502/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs#L300
237+
///
238+
/// Is there a reason this happens twice? Should we need to get rid of one? It should probably happen in the repository only, not here,
239+
/// because in the case of one-to-one, updating a foreign key on the main entity (TodoItem in this case) is sufficient and can indeed be done from the deserializer,
240+
/// but when updating one-to-many or many-to-many, we will need to make extra queries, which is a repository thing.
241+
///
233242
private void SetHasOneForeignKeyValue(object entity, HasOneAttribute hasOneAttr, PropertyInfo foreignKeyProperty, ResourceIdentifierObject rio)
234243
{
235244
var foreignKeyPropertyValue = rio?.Id ?? null;
@@ -274,7 +283,7 @@ private void SetHasOneNavigationPropertyValue(object entity, HasOneAttribute has
274283

275284
private object SetHasManyRelationship(object entity,
276285
PropertyInfo[] entityProperties,
277-
RelationshipAttribute attr,
286+
HasManyAttribute attr,
278287
ContextEntity contextEntity,
279288
Dictionary<string, RelationshipData> relationships,
280289
List<ResourceObject> included = null)
@@ -295,7 +304,7 @@ private object SetHasManyRelationship(object entity,
295304
var convertedCollection = TypeHelper.ConvertCollection(relatedResources, attr.Type);
296305

297306
attr.SetValue(entity, convertedCollection);
298-
307+
_jsonApiContext.RelationshipsToUpdate[attr] = convertedCollection;
299308
_jsonApiContext.HasManyRelationshipPointers.Add(attr, convertedCollection);
300309
}
301310

test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ public UpdatingRelationshipsTests(TestFixture<TestStartup> fixture)
4646
public async Task Can_Update_ToMany_Relationship_By_Patching_Resource()
4747
{
4848
// arrange
49+
_context.TodoItemCollections.RemoveRange(_context.TodoItemCollections);
50+
_context.People.RemoveRange(_context.People);
51+
_context.TodoItems.RemoveRange(_context.TodoItems);
52+
_context.SaveChanges();
53+
4954
var todoCollection = new TodoItemCollection();
5055
todoCollection.TodoItems = new List<TodoItem>();
5156
var person = _personFaker.Generate();
@@ -60,8 +65,6 @@ public async Task Can_Update_ToMany_Relationship_By_Patching_Resource()
6065
_context.AddRange(new TodoItem[] { newTodoItem1, newTodoItem2 });
6166
_context.SaveChanges();
6267

63-
64-
6568
var builder = new WebHostBuilder()
6669
.UseStartup<Startup>();
6770

@@ -102,13 +105,15 @@ public async Task Can_Update_ToMany_Relationship_By_Patching_Resource()
102105
// Act
103106
var response = await client.SendAsync(request);
104107
_context = _fixture.GetService<AppDbContext>();
105-
var updatedTodoItems = _context.TodoItemCollections
106-
.Where(tic => tic.Id == todoCollection.Id)
107-
.Include(tdc => tdc.TodoItems).SingleOrDefault().TodoItems;
108+
var updatedTodoItems = _context.TodoItemCollections.AsNoTracking()
109+
.Where(tic => tic.Id == todoCollection.Id)
110+
.Include(tdc => tdc.TodoItems).SingleOrDefault().TodoItems;
108111

109112

110113
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
111-
Assert.Equal(3, updatedTodoItems.Count);
114+
/// we are expecting two, not three, because the request does
115+
/// a "complete replace".
116+
Assert.Equal(2, updatedTodoItems.Count);
112117
}
113118

114119
[Fact]

0 commit comments

Comments
 (0)