diff --git a/src/JsonApiDotNetCore/Repositories/RepositoryRelationshipUpdateHelper.cs b/src/JsonApiDotNetCore/Repositories/RepositoryRelationshipUpdateHelper.cs index d349abe17c..ab90c51026 100644 --- a/src/JsonApiDotNetCore/Repositories/RepositoryRelationshipUpdateHelper.cs +++ b/src/JsonApiDotNetCore/Repositories/RepositoryRelationshipUpdateHelper.cs @@ -114,6 +114,7 @@ private async Task UpdateManyToManyAsync(IIdentifiable parent, HasManyThroughAtt var newLinks = relationshipIds.Select(x => { + // TODO: [#696] Potential location where we crash if the relationship targets an abstract base class. var link = _resourceFactory.CreateInstance(relationship.ThroughType); relationship.LeftIdProperty.SetValue(link, TypeHelper.ConvertType(parentId, relationship.LeftIdProperty.PropertyType)); relationship.RightIdProperty.SetValue(link, TypeHelper.ConvertType(x, relationship.RightIdProperty.PropertyType)); diff --git a/src/JsonApiDotNetCore/Resources/Annotations/HasManyThroughAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/HasManyThroughAttribute.cs index 16d8cd1075..ae6ad132af 100644 --- a/src/JsonApiDotNetCore/Resources/Annotations/HasManyThroughAttribute.cs +++ b/src/JsonApiDotNetCore/Resources/Annotations/HasManyThroughAttribute.cs @@ -137,6 +137,7 @@ public override void SetValue(object resource, object newValue, IResourceFactory List throughResources = new List(); foreach (IIdentifiable identifiable in (IEnumerable)newValue) { + // TODO: [#696] Potential location where we crash if the relationship targets an abstract base class. object throughResource = resourceFactory.CreateInstance(ThroughType); LeftProperty.SetValue(throughResource, resource); RightProperty.SetValue(throughResource, identifiable); diff --git a/src/JsonApiDotNetCore/Serialization/BaseDeserializer.cs b/src/JsonApiDotNetCore/Serialization/BaseDeserializer.cs index cbe4b793c8..9b417466e0 100644 --- a/src/JsonApiDotNetCore/Serialization/BaseDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/BaseDeserializer.cs @@ -168,15 +168,19 @@ private void SetHasOneRelationship(IIdentifiable resource, var rio = (ResourceIdentifierObject)relationshipData.Data; var relatedId = rio?.Id; + var resourceContext = relationshipData.SingleData == null + ? ResourceContextProvider.GetResourceContext(attr.RightType) + : ResourceContextProvider.GetResourceContext(relationshipData.SingleData.Type); + // this does not make sense in the following case: if we're setting the dependent of a one-to-one relationship, IdentifiablePropertyName should be null. var foreignKeyProperty = resourceProperties.FirstOrDefault(p => p.Name == attr.IdentifiablePropertyName); if (foreignKeyProperty != null) // there is a FK from the current resource pointing to the related object, // i.e. we're populating the relationship from the dependent side. - SetForeignKey(resource, foreignKeyProperty, attr, relatedId); + SetForeignKey(resource, foreignKeyProperty, attr, relatedId, resourceContext.ResourceType); - SetNavigation(resource, attr, relatedId); + SetNavigation(resource, attr, relatedId, resourceContext.ResourceType); // depending on if this base parser is used client-side or server-side, // different additional processing per field needs to be executed. @@ -187,7 +191,7 @@ private void SetHasOneRelationship(IIdentifiable resource, /// Sets the dependent side of a HasOne relationship, which means that a /// foreign key also will to be populated. /// - private void SetForeignKey(IIdentifiable resource, PropertyInfo foreignKey, HasOneAttribute attr, string id) + private void SetForeignKey(IIdentifiable resource, PropertyInfo foreignKey, HasOneAttribute attr, string id, Type relationshipType) { bool foreignKeyPropertyIsNullableType = Nullable.GetUnderlyingType(foreignKey.PropertyType) != null || foreignKey.PropertyType == typeof(string); @@ -198,7 +202,7 @@ private void SetForeignKey(IIdentifiable resource, PropertyInfo foreignKey, HasO throw new FormatException($"Cannot set required relationship identifier '{attr.IdentifiablePropertyName}' to null because it is a non-nullable type."); } - var typedId = TypeHelper.ConvertStringIdToTypedId(attr.Property.PropertyType, id, ResourceFactory); + var typedId = TypeHelper.ConvertStringIdToTypedId(relationshipType /* was: attr.Property.PropertyType */, id, ResourceFactory); foreignKey.SetValue(resource, typedId); } @@ -206,7 +210,8 @@ private void SetForeignKey(IIdentifiable resource, PropertyInfo foreignKey, HasO /// Sets the principal side of a HasOne relationship, which means no /// foreign key is involved. /// - private void SetNavigation(IIdentifiable resource, HasOneAttribute attr, string relatedId) + private void SetNavigation(IIdentifiable resource, HasOneAttribute attr, string relatedId, + Type relationshipType) { if (relatedId == null) { @@ -214,7 +219,7 @@ private void SetNavigation(IIdentifiable resource, HasOneAttribute attr, string } else { - var relatedInstance = (IIdentifiable)ResourceFactory.CreateInstance(attr.RightType); + var relatedInstance = (IIdentifiable)ResourceFactory.CreateInstance(relationshipType /* was: attr.RightType */); relatedInstance.StringId = relatedId; attr.SetValue(resource, relatedInstance, ResourceFactory); } @@ -232,6 +237,7 @@ private void SetHasManyRelationship( { // if the relationship is set to null, no need to set the navigation property to null: this is the default value. var relatedResources = relationshipData.ManyData.Select(rio => { + // TODO: [#696] Potential location where we crash if the relationship targets an abstract base class. var relatedInstance = (IIdentifiable)ResourceFactory.CreateInstance(attr.RightType); relatedInstance.StringId = rio.Id; return relatedInstance; diff --git a/src/JsonApiDotNetCore/Serialization/Client/Internal/ResponseDeserializer.cs b/src/JsonApiDotNetCore/Serialization/Client/Internal/ResponseDeserializer.cs index 277cd0b2c0..8b6beb4651 100644 --- a/src/JsonApiDotNetCore/Serialization/Client/Internal/ResponseDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Client/Internal/ResponseDeserializer.cs @@ -87,6 +87,7 @@ protected override void AfterProcessField(IIdentifiable resource, ResourceFieldA /// private IIdentifiable ParseIncludedRelationship(RelationshipAttribute relationshipAttr, ResourceIdentifierObject relatedResourceIdentifier) { + // TODO: [#696] Potential location where we crash if the relationship targets an abstract base class. var relatedInstance = (IIdentifiable)ResourceFactory.CreateInstance(relationshipAttr.RightType); relatedInstance.StringId = relatedResourceIdentifier.Id;