Skip to content

Commit de82fee

Browse files
committed
fix: merge
2 parents 5cd07d5 + 7aea60c commit de82fee

File tree

10 files changed

+302
-194
lines changed

10 files changed

+302
-194
lines changed

src/Examples/JsonApiDotNetCoreExample/Models/Person.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,6 @@ public Dictionary<string, object> GetMeta(IJsonApiContext context)
6060
{ "authors", new string[] { "Jared Nance" } }
6161
};
6262
}
63+
6364
}
6465
}

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 210 additions & 171 deletions
Large diffs are not rendered by default.

src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Threading.Tasks;
45
using JsonApiDotNetCore.Models;
56
using Microsoft.EntityFrameworkCore;
7+
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
68
using Microsoft.EntityFrameworkCore.Infrastructure;
79
using Microsoft.EntityFrameworkCore.Storage;
810

@@ -32,6 +34,7 @@ public static bool EntityIsTracked(this DbContext context, IIdentifiable entity)
3234
return GetTrackedEntity(context, entity) != null;
3335
}
3436

37+
3538
/// <summary>
3639
/// Determines whether or not EF is already tracking an entity of the same Type and Id
3740
/// and returns that entity.

src/JsonApiDotNetCore/Models/HasOneAttribute.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,13 @@ public HasOneAttribute(string publicName = null, Link documentLinks = Link.All,
5050
/// <param name="newValue">The new property value</param>
5151
public override void SetValue(object resource, object newValue)
5252
{
53-
var propertyName = (newValue?.GetType() == DependentType)
54-
? InternalRelationshipName
55-
: IdentifiablePropertyName;
56-
57-
var propertyInfo = resource
58-
.GetType()
59-
.GetProperty(propertyName);
53+
string propertyName = InternalRelationshipName;
54+
// if we're deleting the relationship (setting it to null),
55+
// we set the foreignKey to null. We could also set the actual property to null,
56+
// but then we would first need to load the current relationship, which requires an extra query.
57+
if (newValue == null) propertyName = IdentifiablePropertyName;
6058

59+
var propertyInfo = resource.GetType().GetProperty(propertyName);
6160
propertyInfo.SetValue(resource, newValue);
6261
}
6362

src/JsonApiDotNetCore/Request/HasManyRelationshipPointers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace JsonApiDotNetCore.Request
3232
/// </summary>
3333
public class HasManyRelationshipPointers
3434
{
35-
private Dictionary<RelationshipAttribute, IList> _hasManyRelationships = new Dictionary<RelationshipAttribute, IList>();
35+
private readonly Dictionary<RelationshipAttribute, IList> _hasManyRelationships = new Dictionary<RelationshipAttribute, IList>();
3636

3737
/// <summary>
3838
/// Add the relationship to the list of relationships that should be

src/JsonApiDotNetCore/Request/HasOneRelationshipPointers.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,18 @@ namespace JsonApiDotNetCore.Request
2828
/// </summary>
2929
public class HasOneRelationshipPointers
3030
{
31-
private Dictionary<RelationshipAttribute, IIdentifiable> _hasOneRelationships = new Dictionary<RelationshipAttribute, IIdentifiable>();
31+
private readonly Dictionary<HasOneAttribute, IIdentifiable> _hasOneRelationships = new Dictionary<HasOneAttribute, IIdentifiable>();
3232

3333
/// <summary>
3434
/// Add the relationship to the list of relationships that should be
3535
/// set in the repository layer.
3636
/// </summary>
37-
public void Add(RelationshipAttribute relationship, IIdentifiable entity)
37+
public void Add(HasOneAttribute relationship, IIdentifiable entity)
3838
=> _hasOneRelationships[relationship] = entity;
3939

4040
/// <summary>
4141
/// Get all the models that should be associated
4242
/// </summary>
43-
public Dictionary<RelationshipAttribute, IIdentifiable> Get() => _hasOneRelationships;
43+
public Dictionary<HasOneAttribute, IIdentifiable> Get() => _hasOneRelationships;
4444
}
4545
}

src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,11 @@ private object SetEntityAttributes(
155155
continue;
156156
var convertedValue = ConvertAttrValue(newValue, attr.PropertyInfo.PropertyType);
157157
attr.SetValue(entity, convertedValue);
158-
_jsonApiContext.AttributesToUpdate[attr] = convertedValue;
158+
/// todo: as a part of the process of decoupling JADNC (specifically
159+
/// through the decoupling IJsonApiContext), we now no longer need to
160+
/// store the updated relationship values in this property. For now
161+
/// just assigning null as value, will remove this property later as a whole.
162+
_jsonApiContext.AttributesToUpdate[attr] = null;
159163
}
160164
}
161165

@@ -249,8 +253,11 @@ private void SetHasOneForeignKeyValue(object entity, HasOneAttribute hasOneAttr,
249253
throw new JsonApiException(400, $"Cannot set required relationship identifier '{hasOneAttr.IdentifiablePropertyName}' to null because it is a non-nullable type.");
250254

251255
var convertedValue = TypeHelper.ConvertType(foreignKeyPropertyValue, foreignKeyProperty.PropertyType);
252-
foreignKeyProperty.SetValue(entity, convertedValue);
253-
_jsonApiContext.RelationshipsToUpdate[hasOneAttr] = convertedValue;
256+
/// todo: as a part of the process of decoupling JADNC (specifically
257+
/// through the decoupling IJsonApiContext), we now no longer need to
258+
/// store the updated relationship values in this property. For now
259+
/// just assigning null as value, will remove this property later as a whole.
260+
if (convertedValue == null ) _jsonApiContext.HasOneRelationshipPointers.Add(hasOneAttr, null);
254261
}
255262
}
256263

@@ -270,10 +277,11 @@ private void SetHasOneNavigationPropertyValue(object entity, HasOneAttribute has
270277
if (includedRelationshipObject != null)
271278
hasOneAttr.SetValue(entity, includedRelationshipObject);
272279

273-
// we need to store the fact that this relationship was included in the payload
274-
// for EF, the repository will use these pointers to make ensure we don't try to
275-
// create resources if they already exist, we just need to create the relationship
276-
_jsonApiContext.HasOneRelationshipPointers.Add(hasOneAttr, includedRelationshipObject);
280+
/// todo: as a part of the process of decoupling JADNC (specifically
281+
/// through the decoupling IJsonApiContext), we now no longer need to
282+
/// store the updated relationship values in this property. For now
283+
/// just assigning null as value, will remove this property later as a whole.
284+
_jsonApiContext.HasOneRelationshipPointers.Add(hasOneAttr, null);
277285
}
278286
}
279287

@@ -300,8 +308,11 @@ private object SetHasManyRelationship(object entity,
300308
var convertedCollection = TypeHelper.ConvertCollection(relatedResources, attr.DependentType);
301309

302310
attr.SetValue(entity, convertedCollection);
303-
_jsonApiContext.RelationshipsToUpdate[attr] = convertedCollection;
304-
_jsonApiContext.HasManyRelationshipPointers.Add(attr, convertedCollection);
311+
/// todo: as a part of the process of decoupling JADNC (specifically
312+
/// through the decoupling IJsonApiContext), we now no longer need to
313+
/// store the updated relationship values in this property. For now
314+
/// just assigning null as value, will remove this property later as a whole.
315+
_jsonApiContext.HasManyRelationshipPointers.Add(attr, null);
305316
}
306317

307318
return entity;

src/JsonApiDotNetCore/Services/IJsonApiContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public interface IUpdateRequest
3535
/// Any relationships that were included in a PATCH request.
3636
/// Only the relationships in this dictionary should be updated.
3737
/// </summary>
38-
Dictionary<RelationshipAttribute, object> RelationshipsToUpdate { get; set; }
38+
Dictionary<RelationshipAttribute, object> RelationshipsToUpdate { get; }
3939
}
4040

4141
public interface IJsonApiRequest : IJsonApiApplication, IUpdateRequest, IQueryRequest

src/JsonApiDotNetCore/Services/JsonApiContext.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using JsonApiDotNetCore.Builders;
45
using JsonApiDotNetCore.Configuration;
56
using JsonApiDotNetCore.Internal;
@@ -52,7 +53,15 @@ public JsonApiContext(
5253
public bool IsBulkOperationRequest { get; set; }
5354

5455
public Dictionary<AttrAttribute, object> AttributesToUpdate { get; set; } = new Dictionary<AttrAttribute, object>();
55-
public Dictionary<RelationshipAttribute, object> RelationshipsToUpdate { get; set; } = new Dictionary<RelationshipAttribute, object>();
56+
public Dictionary<RelationshipAttribute, object> RelationshipsToUpdate { get => GetRelationshipsToUpdate(); }
57+
58+
private Dictionary<RelationshipAttribute, object> GetRelationshipsToUpdate()
59+
{
60+
var hasOneEntries = HasOneRelationshipPointers.Get().ToDictionary(kvp => (RelationshipAttribute)kvp.Key, kvp => (object)kvp.Value);
61+
var hasManyEntries = HasManyRelationshipPointers.Get().ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value);
62+
return hasOneEntries.Union(hasManyEntries).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
63+
}
64+
5665
public HasManyRelationshipPointers HasManyRelationshipPointers { get; private set; } = new HasManyRelationshipPointers();
5766
public HasOneRelationshipPointers HasOneRelationshipPointers { get; private set; } = new HasOneRelationshipPointers();
5867

@@ -138,7 +147,6 @@ public void BeginOperation()
138147
{
139148
IncludedRelationships = new List<string>();
140149
AttributesToUpdate = new Dictionary<AttrAttribute, object>();
141-
RelationshipsToUpdate = new Dictionary<RelationshipAttribute, object>();
142150
HasManyRelationshipPointers = new HasManyRelationshipPointers();
143151
HasOneRelationshipPointers = new HasOneRelationshipPointers();
144152
}

test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,5 +621,52 @@ public async Task Can_Delete_Relationship_By_Patching_Relationship()
621621
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
622622
Assert.Null(todoItemResult.Owner);
623623
}
624+
625+
[Fact]
626+
public async Task Updating_ToOne_Relationship_With_Implicit_Remove()
627+
{
628+
// Arrange
629+
var context = _fixture.GetService<AppDbContext>();
630+
var passport = new Passport();
631+
var person1 = _personFaker.Generate();
632+
person1.Passport = passport;
633+
var person2 = _personFaker.Generate();
634+
context.People.AddRange(new List<Person>() { person1, person2 });
635+
await context.SaveChangesAsync();
636+
637+
var content = new
638+
{
639+
data = new
640+
{
641+
type = "people",
642+
id = person2.Id,
643+
relationships = new Dictionary<string, object>
644+
{
645+
{ "passport", new
646+
{
647+
data = new { type = "passports", id = $"{person1.PassportId}" }
648+
}
649+
}
650+
}
651+
}
652+
};
653+
654+
var httpMethod = new HttpMethod("PATCH");
655+
var route = $"/api/v1/people/{person2.Id}";
656+
var request = new HttpRequestMessage(httpMethod, route);
657+
658+
string serializedContent = JsonConvert.SerializeObject(content);
659+
request.Content = new StringContent(serializedContent);
660+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
661+
662+
// Act
663+
var response = await _fixture.Client.SendAsync(request);
664+
665+
// Assert
666+
var body = await response.Content.ReadAsStringAsync();
667+
// this test currently fails, will be adressed in next PR
668+
//Assert.True(HttpStatusCode.OK == response.StatusCode, $"{route} returned {response.StatusCode} status code with payload: {body}");
669+
670+
}
624671
}
625672
}

0 commit comments

Comments
 (0)