Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9059560

Browse files
Merge pull request #493 from wisepotato/fix/#492
Fix/#492
2 parents 8f685a6 + e7ee2c2 commit 9059560

File tree

6 files changed

+99
-16
lines changed

6 files changed

+99
-16
lines changed

‎JsonApiDotnetCore.sln‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ Global
190190
{6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x64.ActiveCfg = Release|Any CPU
191191
{6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x64.Build.0 = Release|Any CPU
192192
{6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.ActiveCfg = Release|Any CPU
193-
{6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.Build.0 = Release|Any CPU\
193+
{6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.Build.0 = Release|Any CPU
194194
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
195195
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
196196
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -203,6 +203,7 @@ Global
203203
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x64.Build.0 = Release|Any CPU
204204
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x86.ActiveCfg = Release|Any CPU
205205
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x86.Build.0 = Release|Any CPU
206+
{DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
206207
EndGlobalSection
207208
GlobalSection(SolutionProperties) = preSolution
208209
HideSolutionNode = FALSE

‎src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs‎

Lines changed: 19 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,20 @@ 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+
/// Also, we currently do not support the same for many-to-many
307+
if (relationship.Key is HasManyAttribute && !(relationship.Key is HasManyThroughAttribute))
308+
await _context.Entry(oldEntity).Collection(relationship.Key.InternalRelationshipName).LoadAsync();
309+
relationship.Key.SetValue(oldEntity, relationship.Value); // article.tags = nieuwe lijst
310+
}
311+
}
304312
await _context.SaveChangesAsync();
305-
306313
return oldEntity;
307314
}
308315

@@ -366,7 +373,7 @@ public virtual IQueryable<TEntity> Include(IQueryable<TEntity> entities, string
366373
? relationship.RelationshipPath
367374
: $"{internalRelationshipPath}.{relationship.RelationshipPath}";
368375

369-
if(i < relationshipChain.Length)
376+
if(i < relationshipChain.Length)
370377
entity = _jsonApiContext.ResourceGraph.GetContextEntity(relationship.Type);
371378
}
372379

‎src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs‎

Lines changed: 3 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;
@@ -274,7 +274,7 @@ private void SetHasOneNavigationPropertyValue(object entity, HasOneAttribute has
274274

275275
private object SetHasManyRelationship(object entity,
276276
PropertyInfo[] entityProperties,
277-
RelationshipAttribute attr,
277+
HasManyAttribute attr,
278278
ContextEntity contextEntity,
279279
Dictionary<string, RelationshipData> relationships,
280280
List<ResourceObject> included = null)
@@ -295,7 +295,7 @@ private object SetHasManyRelationship(object entity,
295295
var convertedCollection = TypeHelper.ConvertCollection(relatedResources, attr.Type);
296296

297297
attr.SetValue(entity, convertedCollection);
298-
298+
_jsonApiContext.RelationshipsToUpdate[attr]=convertedCollection;
299299
_jsonApiContext.HasManyRelationshipPointers.Add(attr, convertedCollection);
300300
}
301301

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ public async Task GET_Included_DoesNot_Duplicate_Records_ForMultipleRelationship
181181
public async Task GET_Included_DoesNot_Duplicate_Records_If_HasOne_Exists_Twice()
182182
{
183183
// arrange
184+
_context.TodoItemCollections.RemoveRange(_context.TodoItemCollections);
184185
_context.People.RemoveRange(_context.People); // ensure all people have todo-items
185186
_context.TodoItems.RemoveRange(_context.TodoItems);
186187
var person = _personFaker.Generate();

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,78 @@ public UpdatingRelationshipsTests(TestFixture<TestStartup> fixture)
3737
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
3838
.RuleFor(t => t.Ordinal, f => f.Random.Number())
3939
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
40+
41+
42+
}
43+
44+
45+
[Fact]
46+
public async Task Can_Update_ToMany_Relationship_By_Patching_Resource()
47+
{
48+
// arrange
49+
var todoCollection = new TodoItemCollection();
50+
todoCollection.TodoItems = new List<TodoItem>();
51+
var person = _personFaker.Generate();
52+
var todoItem = _todoItemFaker.Generate();
53+
todoCollection.Owner = person;
54+
todoCollection.TodoItems.Add(todoItem);
55+
_context.TodoItemCollections.Add(todoCollection);
56+
_context.SaveChanges();
57+
58+
var newTodoItem1 = _todoItemFaker.Generate();
59+
var newTodoItem2 = _todoItemFaker.Generate();
60+
_context.AddRange(new TodoItem[] { newTodoItem1, newTodoItem2 });
61+
_context.SaveChanges();
62+
63+
var builder = new WebHostBuilder()
64+
.UseStartup<Startup>();
65+
66+
var server = new TestServer(builder);
67+
var client = server.CreateClient();
68+
69+
70+
var content = new
71+
{
72+
data = new
73+
{
74+
type = "todo-collections",
75+
id = todoCollection.Id,
76+
relationships = new Dictionary<string, object>
77+
{
78+
{ "todo-items", new
79+
{
80+
data = new object[]
81+
{
82+
new { type = "todo-items", id = $"{newTodoItem1.Id}" },
83+
new { type = "todo-items", id = $"{newTodoItem2.Id}" }
84+
}
85+
86+
}
87+
},
88+
}
89+
}
90+
};
91+
92+
var httpMethod = new HttpMethod("PATCH");
93+
var route = $"/api/v1/todo-collections/{todoCollection.Id}";
94+
var request = new HttpRequestMessage(httpMethod, route);
95+
96+
string serializedContent = JsonConvert.SerializeObject(content);
97+
request.Content = new StringContent(serializedContent);
98+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
99+
100+
// Act
101+
var response = await client.SendAsync(request);
102+
_context = _fixture.GetService<AppDbContext>();
103+
var updatedTodoItems = _context.TodoItemCollections.AsNoTracking()
104+
.Where(tic => tic.Id == todoCollection.Id)
105+
.Include(tdc => tdc.TodoItems).SingleOrDefault().TodoItems;
106+
107+
108+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
109+
/// we are expecting two, not three, because the request does
110+
/// a "complete replace".
111+
Assert.Equal(2, updatedTodoItems.Count);
40112
}
41113

42114
[Fact]

‎test/UnitTests/Serialization/JsonApiDeSerializerTests.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ public void Can_Deserialize_Object_With_HasManyRelationship()
361361
jsonApiContextMock.SetupAllProperties();
362362
jsonApiContextMock.Setup(m => m.ResourceGraph).Returns(resourceGraph);
363363
jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary<AttrAttribute, object>());
364+
jsonApiContextMock.Setup(m => m.RelationshipsToUpdate).Returns(new Dictionary<RelationshipAttribute, object>());
364365
jsonApiContextMock.Setup(m => m.HasManyRelationshipPointers).Returns(new HasManyRelationshipPointers());
365366

366367
var jsonApiOptions = new JsonApiOptions();
@@ -414,6 +415,7 @@ public void Sets_Attribute_Values_On_Included_HasMany_Relationships()
414415
jsonApiContextMock.SetupAllProperties();
415416
jsonApiContextMock.Setup(m => m.ResourceGraph).Returns(resourceGraph);
416417
jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary<AttrAttribute, object>());
418+
jsonApiContextMock.Setup(m => m.RelationshipsToUpdate).Returns(new Dictionary<RelationshipAttribute, object>());
417419
jsonApiContextMock.Setup(m => m.HasManyRelationshipPointers).Returns(new HasManyRelationshipPointers());
418420

419421
var jsonApiOptions = new JsonApiOptions();

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /