Skip to content

Commit 89d55b6

Browse files
committed
fix(json-api-dotnet#494): added another fix that also addresses an error in json-api-dotnet#492)
1 parent 457781c commit 89d55b6

File tree

3 files changed

+86
-13
lines changed

3 files changed

+86
-13
lines changed

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,15 @@ private void AttachHasMany(TEntity entity, HasManyAttribute relationship, IList
225225
var relatedList = (IList)entity.GetType().GetProperty(relationship.EntityPropertyName)?.GetValue(entity);
226226
foreach (var related in relatedList)
227227
{
228-
_context.Entry(related).State = EntityState.Unchanged;
228+
if (_context.EntityIsTracked(related as IIdentifiable) == false)
229+
_context.Entry(related).State = EntityState.Unchanged;
229230
}
230231
}
231232
else
232233
{
233234
foreach (var pointer in pointers)
234235
{
236+
if (_context.EntityIsTracked(pointer as IIdentifiable) == false)
235237
_context.Entry(pointer).State = EntityState.Unchanged;
236238
}
237239
}
@@ -307,21 +309,27 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity entity)
307309
{
308310
await _context.Entry(oldEntity).Collection(throughAttribute.InternalThroughName).LoadAsync();
309311
}
310-
else if (relationship.Key is HasManyAttribute)
311-
{
312-
await _context.Entry(oldEntity).Collection(relationship.Key.InternalRelationshipName).LoadAsync();
313-
}
314312
}
313+
314+
/// @HACK @TODO: It is inconsistent that for many-to-many, the new relationship value
315+
/// is assigned in AttachRelationships() helper fn below, but not for
316+
/// one-to-many and one-to-one (we need to do that manually as done below).
317+
/// Simultaneously, for a proper working "complete replacement", in the case of many-to-many
318+
/// we need to LoadAsync() BEFORE calling AttachRelationships(), but for one-to-many we
319+
/// need to do it AFTER AttachRelationships or we we'll get entity tracking errors
320+
/// This really needs a refactor.
315321
AttachRelationships(oldEntity);
316322

317-
/// @TODO: It it not consistent that for many-to-many, the new relationship value
318-
/// is assigned in AttachRelationships() helperfunction, whereas for
319-
/// one-to-many and one-to-one, we need to do it manually as below.
320-
/// As a result, we need to loop over RelationshipsToUpdate a second time.
321323
foreach (var relationship in _jsonApiContext.RelationshipsToUpdate)
322324
{
323-
if (!(relationship.Key is HasManyThroughAttribute))
324-
{
325+
326+
if ((relationship.Key.TypeId as Type).IsAssignableFrom(typeof(HasOneAttribute)))
327+
{
328+
relationship.Key.SetValue(oldEntity, relationship.Value);
329+
}
330+
if ((relationship.Key.TypeId as Type).IsAssignableFrom(typeof(HasManyAttribute)))
331+
{
332+
await _context.Entry(oldEntity).Collection(relationship.Key.InternalRelationshipName).LoadAsync();
325333
relationship.Key.SetValue(oldEntity, relationship.Value);
326334
}
327335
}

test/JsonApiDotNetCoreExampleTests/Acceptance/ManyToManyTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ public async Task Can_Update_Many_To_Many_With_Complete_Replacement()
313313
var tag = persistedArticle.ArticleTags.Select(at => at.Tag).Single();
314314
Assert.Equal(secondTag.Id, tag.Id);
315315
}
316+
316317
[Fact]
317318
public async Task Can_Update_Many_To_Many_With_Complete_Replacement_With_Overlap()
318319
{

test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ public UpdatingRelationshipsTests(TestFixture<TestStartup> fixture)
4141

4242
}
4343

44-
4544
[Fact]
4645
public async Task Can_Update_ToMany_Relationship_By_Patching_Resource()
4746
{
@@ -66,7 +65,6 @@ public async Task Can_Update_ToMany_Relationship_By_Patching_Resource()
6665
var server = new TestServer(builder);
6766
var client = server.CreateClient();
6867

69-
7068
var content = new
7169
{
7270
data = new
@@ -111,6 +109,72 @@ public async Task Can_Update_ToMany_Relationship_By_Patching_Resource()
111109
Assert.Equal(2, updatedTodoItems.Count);
112110
}
113111

112+
[Fact]
113+
public async Task Can_Update_ToMany_Relationship_By_Patching_Resource_With_Overlap()
114+
{
115+
// arrange
116+
var todoCollection = new TodoItemCollection();
117+
todoCollection.TodoItems = new List<TodoItem>();
118+
var person = _personFaker.Generate();
119+
var todoItem1 = _todoItemFaker.Generate();
120+
var todoItem2 = _todoItemFaker.Generate();
121+
todoCollection.Owner = person;
122+
todoCollection.TodoItems.Add(todoItem1);
123+
todoCollection.TodoItems.Add(todoItem2);
124+
_context.TodoItemCollections.Add(todoCollection);
125+
_context.SaveChanges();
126+
127+
var builder = new WebHostBuilder()
128+
.UseStartup<Startup>();
129+
130+
var server = new TestServer(builder);
131+
var client = server.CreateClient();
132+
133+
134+
var content = new
135+
{
136+
data = new
137+
{
138+
type = "todo-collections",
139+
id = todoCollection.Id,
140+
relationships = new Dictionary<string, object>
141+
{
142+
{ "todo-items", new
143+
{
144+
data = new object[]
145+
{
146+
new { type = "todo-items", id = $"{todoItem1.Id}" },
147+
new { type = "todo-items", id = $"{todoItem2.Id}" }
148+
}
149+
150+
}
151+
},
152+
}
153+
}
154+
};
155+
156+
var httpMethod = new HttpMethod("PATCH");
157+
var route = $"/api/v1/todo-collections/{todoCollection.Id}";
158+
var request = new HttpRequestMessage(httpMethod, route);
159+
160+
string serializedContent = JsonConvert.SerializeObject(content);
161+
request.Content = new StringContent(serializedContent);
162+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
163+
164+
// Act
165+
var response = await client.SendAsync(request);
166+
167+
168+
_context = _fixture.GetService<AppDbContext>();
169+
var updatedTodoItems = _context.TodoItemCollections.AsNoTracking()
170+
.Where(tic => tic.Id == todoCollection.Id)
171+
.Include(tdc => tdc.TodoItems).SingleOrDefault().TodoItems;
172+
173+
174+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
175+
Assert.Equal(2, updatedTodoItems.Count);
176+
}
177+
114178
[Fact]
115179
public async Task Can_Update_ToMany_Relationship_ThroughLink()
116180
{

0 commit comments

Comments
 (0)