private object SetHasOneRelationship(object entity,
                                             PropertyInfo[] entityProperties,
                                             HasOneAttribute attr,
                                             ContextEntity contextEntity,
                                             Dictionary <string, RelationshipData> relationships,
                                             List <DocumentData> included = null)
        {
            var relationshipName = attr.PublicRelationshipName;

            if (relationships.TryGetValue(relationshipName, out RelationshipData relationshipData) == false)
            {
                return(entity);
            }

            var rio = (ResourceIdentifierObject)relationshipData.ExposedData;

            var foreignKey         = attr.IdentifiablePropertyName;
            var foreignKeyProperty = entityProperties.FirstOrDefault(p => p.Name == foreignKey);

            if (foreignKeyProperty == null && rio == null)
            {
                return(entity);
            }

            SetHasOneForeignKeyValue(entity, attr, foreignKeyProperty, rio);
            SetHasOneNavigationPropertyValue(entity, attr, rio, included);

            return(entity);
        }
Exemple #2
0
        /// <summary>
        /// Sets a HasOne relationship on a parsed entity. If present, also
        /// populates the foreign key.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="entityProperties"></param>
        /// <param name="attr"></param>
        /// <param name="relationshipData"></param>
        /// <returns></returns>
        private object SetHasOneRelationship(IIdentifiable entity,
                                             PropertyInfo[] entityProperties,
                                             HasOneAttribute attr,
                                             RelationshipEntry relationshipData)
        {
            var rio       = (ResourceIdentifierObject)relationshipData.Data;
            var relatedId = rio?.Id ?? null;

            // 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 = entityProperties.FirstOrDefault(p => p.Name == attr.IdentifiablePropertyName);

            if (foreignKeyProperty != null)
            {
                /// there is a FK from the current entity pointing to the related object,
                /// i.e. we're populating the relationship from the dependent side.
                SetForeignKey(entity, foreignKeyProperty, attr, relatedId);
            }

            SetNavigation(entity, attr, relatedId);

            /// depending on if this base parser is used client-side or server-side,
            /// different additional processing per field needs to be executed.
            AfterProcessField(entity, attr, relationshipData);

            return(entity);
        }
Exemple #3
0
 public RelationshipDictionaryTests()
 {
     FirstToOneAttr = new HasOneAttribute("firstToOne")
     {
         LeftType     = typeof(Dummy),
         RightType    = typeof(ToOne),
         PropertyInfo = typeof(Dummy).GetProperty(nameof(Dummy.FirstToOne))
     };
     SecondToOneAttr = new HasOneAttribute("secondToOne")
     {
         LeftType     = typeof(Dummy),
         RightType    = typeof(ToOne),
         PropertyInfo = typeof(Dummy).GetProperty(nameof(Dummy.SecondToOne))
     };
     ToManyAttr = new HasManyAttribute("toManies")
     {
         LeftType     = typeof(Dummy),
         RightType    = typeof(ToMany),
         PropertyInfo = typeof(Dummy).GetProperty(nameof(Dummy.ToManies))
     };
     Relationships.Add(FirstToOneAttr, FirstToOnesEntities);
     Relationships.Add(SecondToOneAttr, SecondToOnesEntities);
     Relationships.Add(ToManyAttr, ToManiesEntities);
     AllEntities = new HashSet <Dummy>(FirstToOnesEntities.Union(SecondToOnesEntities).Union(ToManiesEntities).Union(NoRelationshipsEntities));
 }
Exemple #4
0
        /// <summary>
        /// Sets a HasOne relationship on a parsed resource. If present, also
        /// populates the foreign key.
        /// </summary>
        private void SetHasOneRelationship(IIdentifiable resource,
                                           PropertyInfo[] resourceProperties,
                                           HasOneAttribute attr,
                                           RelationshipEntry relationshipData)
        {
            var rio       = (ResourceIdentifierObject)relationshipData.Data;
            var relatedId = rio?.Id;

            var relationshipType = relationshipData.SingleData == null
                ? attr.RightType
                : ResourceContextProvider.GetResourceContext(relationshipData.SingleData.Type).ResourceType;

            // 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, relationshipType);
            }

            SetNavigation(resource, attr, relatedId, relationshipType);

            // depending on if this base parser is used client-side or server-side,
            // different additional processing per field needs to be executed.
            AfterProcessField(resource, attr, relationshipData);
        }
Exemple #5
0
 public RelationshipDictionaryTests()
 {
     _firstToOneAttr = new HasOneAttribute
     {
         PublicName = "firstToOne",
         LeftType   = typeof(Dummy),
         RightType  = typeof(ToOne),
         Property   = typeof(Dummy).GetProperty(nameof(Dummy.FirstToOne))
     };
     _secondToOneAttr = new HasOneAttribute
     {
         PublicName = "secondToOne",
         LeftType   = typeof(Dummy),
         RightType  = typeof(ToOne),
         Property   = typeof(Dummy).GetProperty(nameof(Dummy.SecondToOne))
     };
     _toManyAttr = new HasManyAttribute
     {
         PublicName = "toManies",
         LeftType   = typeof(Dummy),
         RightType  = typeof(ToMany),
         Property   = typeof(Dummy).GetProperty(nameof(Dummy.ToManies))
     };
     _relationships.Add(_firstToOneAttr, _firstToOnesResources);
     _relationships.Add(_secondToOneAttr, _secondToOnesResources);
     _relationships.Add(_toManyAttr, _toManiesResources);
     _allResources = new HashSet <Dummy>(_firstToOnesResources.Union(_secondToOnesResources).Union(_toManiesResources).Union(_noRelationshipsResources));
 }
        public void HasOneAttribute_Equals_Returns_False_When_Different_Name()
        {
            var a = new HasOneAttribute("test");
            var b = new HasOneAttribute("test2");

            Assert.NotEqual(a, b);
        }
        public void Applies_cascading_settings_for_relationship_links(LinkTypes linksInRelationshipAttribute, LinkTypes linksInResourceContext,
                                                                      LinkTypes linksInOptions, LinkTypes expected)
        {
            // Arrange
            var exampleResourceContext = new ResourceContext
            {
                PublicName        = nameof(ExampleResource),
                ResourceType      = typeof(ExampleResource),
                RelationshipLinks = linksInResourceContext
            };

            var options = new JsonApiOptions
            {
                RelationshipLinks = linksInOptions
            };

            var request                   = new JsonApiRequest();
            var paginationContext         = new PaginationContext();
            var resourceGraph             = new ResourceGraph(exampleResourceContext.AsArray());
            var httpContextAccessor       = new FakeHttpContextAccessor();
            var linkGenerator             = new FakeLinkGenerator();
            var controllerResourceMapping = new FakeControllerResourceMapping();

            var linkBuilder = new LinkBuilder(options, request, paginationContext, resourceGraph, httpContextAccessor, linkGenerator,
                                              controllerResourceMapping);

            var relationship = new HasOneAttribute
            {
                Links = linksInRelationshipAttribute
            };

            // Act
            RelationshipLinks relationshipLinks = linkBuilder.GetRelationshipLinks(relationship, new ExampleResource());

            // Assert
            if (expected == LinkTypes.None)
            {
                relationshipLinks.Should().BeNull();
            }
            else
            {
                if (expected.HasFlag(LinkTypes.Self))
                {
                    relationshipLinks.Self.Should().NotBeNull();
                }
                else
                {
                    relationshipLinks.Self.Should().BeNull();
                }

                if (expected.HasFlag(LinkTypes.Related))
                {
                    relationshipLinks.Related.Should().NotBeNull();
                }
                else
                {
                    relationshipLinks.Related.Should().BeNull();
                }
            }
        }
        public void HasOneAttribute_Equals_Returns_True_When_Same_Name()
        {
            var a = new HasOneAttribute("test");
            var b = new HasOneAttribute("test");

            Assert.Equal(a, b);
        }
Exemple #9
0
        private void SetHasOneForeignKeyValue(object entity, HasOneAttribute hasOneAttr, PropertyInfo foreignKeyProperty, ResourceIdentifierObject rio)
        {
            var foreignKeyPropertyValue = rio?.Id ?? null;

            if (foreignKeyProperty != null)
            {
                // in the case of the HasOne independent side of the relationship, we should still create the shell entity on the other side
                // we should not actually require the resource to have a foreign key (be the dependent side of the relationship)

                // e.g. PATCH /articles
                // {... { "relationships":{ "Owner": { "data": null } } } }
                bool foreignKeyPropertyIsNullableType = Nullable.GetUnderlyingType(foreignKeyProperty.PropertyType) != null ||
                                                        foreignKeyProperty.PropertyType == typeof(string);
                if (rio == null && !foreignKeyPropertyIsNullableType)
                {
                    throw new JsonApiException(400, $"Cannot set required relationship identifier '{hasOneAttr.IdentifiablePropertyName}' to null because it is a non-nullable type.");
                }

                var convertedValue = TypeHelper.ConvertType(foreignKeyPropertyValue, foreignKeyProperty.PropertyType);
                /// todo: as a part of the process of decoupling JADNC (specifically
                /// through the decoupling IJsonApiContext), we now no longer need to
                /// store the updated relationship values in this property. For now
                /// just assigning null as value, will remove this property later as a whole.
                /// see #512
                if (convertedValue == null)
                {
                    _jsonApiContext.HasOneRelationshipPointers.Add(hasOneAttr, null);
                }
            }
        }
 public RelationshipDictionaryTests()
 {
     FirstToOneAttr = new HasOneAttribute
     {
         PublicName = "firstToOne",
         LeftType   = typeof(Dummy),
         RightType  = typeof(ToOne),
         Property   = typeof(Dummy).GetProperty(nameof(Dummy.FirstToOne))
     };
     SecondToOneAttr = new HasOneAttribute
     {
         PublicName = "secondToOne",
         LeftType   = typeof(Dummy),
         RightType  = typeof(ToOne),
         Property   = typeof(Dummy).GetProperty(nameof(Dummy.SecondToOne))
     };
     ToManyAttr = new HasManyAttribute
     {
         PublicName = "toManies",
         LeftType   = typeof(Dummy),
         RightType  = typeof(ToMany),
         Property   = typeof(Dummy).GetProperty(nameof(Dummy.ToManies))
     };
     Relationships.Add(FirstToOneAttr, FirstToOnesResources);
     Relationships.Add(SecondToOneAttr, SecondToOnesResources);
     Relationships.Add(ToManyAttr, ToManiesResources);
     AllResources = new HashSet <Dummy>(FirstToOnesResources.Union(SecondToOnesResources).Union(ToManiesResources).Union(NoRelationshipsResources));
 }
Exemple #11
0
        public void BuildRelationshipLinks_GlobalResourceAndAttrConfiguration_ExpectedLinks(Link global,
                                                                                            Link resource,
                                                                                            Link relationship,
                                                                                            object expectedSelfLink,
                                                                                            object expectedRelatedLink)
        {
            // Arrange
            var config          = GetConfiguration(relationshipLinks: global);
            var primaryResource = GetArticleResourceContext(relationshipLinks: resource);

            _provider.Setup(m => m.GetResourceContext(typeof(Article))).Returns(primaryResource);
            var builder = new LinkBuilder(config, GetRequestManager(), null, _provider.Object, _queryStringAccessor);
            var attr    = new HasOneAttribute(links: relationship)
            {
                RightType = typeof(Author), PublicRelationshipName = "author"
            };

            // Act
            var links = builder.GetRelationshipLinks(attr, new Article {
                Id = _baseId
            });

            // Assert
            if (expectedSelfLink == null && expectedRelatedLink == null)
            {
                Assert.Null(links);
            }
            else
            {
                Assert.Equal(expectedSelfLink, links.Self);
                Assert.Equal(expectedRelatedLink, links.Related);
            }
        }
 public RelationshipDictionaryTests()
 {
     FirstToOneAttr = new HasOneAttribute("first-to-one")
     {
         PrincipalType            = typeof(Dummy),
         DependentType            = typeof(ToOne),
         InternalRelationshipName = "FirstToOne"
     };
     SecondToOneAttr = new HasOneAttribute("second-to-one")
     {
         PrincipalType            = typeof(Dummy),
         DependentType            = typeof(ToOne),
         InternalRelationshipName = "SecondToOne"
     };
     ToManyAttr = new HasManyAttribute("to-manies")
     {
         PrincipalType            = typeof(Dummy),
         DependentType            = typeof(ToMany),
         InternalRelationshipName = "ToManies"
     };
     Relationships.Add(FirstToOneAttr, FirstToOnesEntities);
     Relationships.Add(SecondToOneAttr, SecondToOnesEntities);
     Relationships.Add(ToManyAttr, ToManiesEntities);
     AllEntities = new HashSet <Dummy>(FirstToOnesEntities.Union(SecondToOnesEntities).Union(ToManiesEntities).Union(NoRelationshipsEntities));
 }
        public void HasManyAttribute_Does_Not_Equal_HasOneAttribute_With_Same_Name()
        {
            RelationshipAttribute a = new HasManyAttribute("test");
            RelationshipAttribute b = new HasOneAttribute("test");

            Assert.NotEqual(a, b);
            Assert.NotEqual(b, a);
        }
Exemple #14
0
        /// <summary>
        /// Builds a <see cref="ResourceIdentifierObject"/> for a HasOne relationship.
        /// </summary>
        private ResourceIdentifierObject GetRelatedResourceLinkageForHasOne(HasOneAttribute relationship, IIdentifiable resource)
        {
            var relatedResource = (IIdentifiable)relationship.GetValue(resource);

            if (relatedResource != null)
            {
                return(GetResourceIdentifier(relatedResource));
            }

            return(null);
        }
Exemple #15
0
        /// <summary>
        /// Checks if the to-one relationship is required by checking if the foreign key is nullable.
        /// </summary>
        private bool IsRequiredToOneRelationship(HasOneAttribute attr, IIdentifiable entity)
        {
            var foreignKey = entity.GetType().GetProperty(attr.IdentifiablePropertyName);

            if (foreignKey != null && Nullable.GetUnderlyingType(foreignKey.PropertyType) == null)
            {
                return(true);
            }

            return(false);
        }
Exemple #16
0
        private object SetHasOneRelationship(object entity,
                                             PropertyInfo[] entityProperties,
                                             HasOneAttribute attr,
                                             ContextEntity contextEntity,
                                             Dictionary <string, RelationshipData> relationships,
                                             List <ResourceObject> included = null,
                                             List <string> includePaths     = null)
        {
            var relationshipName = attr.PublicRelationshipName;

            if (relationships.TryGetValue(relationshipName, out RelationshipData relationshipData) == false)
            {
                return(entity);
            }

            var rio = (ResourceIdentifierObject)relationshipData.ExposedData;

            var foreignKey         = attr.IdentifiablePropertyName;
            var foreignKeyProperty = entityProperties.FirstOrDefault(p => p.Name == foreignKey);

            if (foreignKeyProperty == null && rio == null)
            {
                return(entity);
            }

            SetHasOneForeignKeyValue(entity, attr, foreignKeyProperty, rio);
            SetHasOneNavigationPropertyValue(entity, attr, rio, included);

            // recursive call ...
            if (included != null)
            {
                var navigationPropertyValue = attr.GetValue(entity);

                var resourceGraphEntity = _jsonApiContext.ResourceGraph.GetContextEntity(attr.DependentType);
                if (navigationPropertyValue != null && resourceGraphEntity != null)

                {
                    var includedResource = included.SingleOrDefault(r => r.Type == rio.Type && r.Id == rio.Id);
                    if (includedResource != null)
                    {
                        var paths = includePaths?.Select(x =>
                        {
                            var tmp = x.Split('.').ToList();
                            tmp.RemoveAt(0);
                            return(string.Join(".", tmp));
                        }).ToList();
                        SetRelationships(navigationPropertyValue, resourceGraphEntity, includedResource.Relationships, included, paths);
                    }
                }
            }

            return(entity);
        }
Exemple #17
0
 /// <summary>
 /// Sets the principal side of a HasOne relationship, which means no
 /// foreign key is involved
 /// </summary>
 private void SetNavigation(IIdentifiable entity, HasOneAttribute attr, string relatedId)
 {
     if (relatedId == null)
     {
         attr.SetValue(entity, null);
     }
     else
     {
         var relatedInstance = attr.RightType.New <IIdentifiable>();
         relatedInstance.StringId = relatedId;
         attr.SetValue(entity, relatedInstance);
     }
 }
 /// <summary>
 /// Sets the principal side of a HasOne relationship, which means no
 /// foreign key is involved
 /// </summary>
 private void SetNavigation(IIdentifiable entity, HasOneAttribute attr, string relatedId)
 {
     if (relatedId == null)
     {
         attr.SetValue(entity, null, _resourceFactory);
     }
     else
     {
         var relatedInstance = (IIdentifiable)_resourceFactory.CreateInstance(attr.RightType);
         relatedInstance.StringId = relatedId;
         attr.SetValue(entity, relatedInstance, _resourceFactory);
     }
 }
        public void HasOneAttribute_Equals_Returns_True_When_Same_Name()
        {
            var attribute1 = new HasOneAttribute
            {
                PublicName = "test"
            };

            var attribute2 = new HasOneAttribute
            {
                PublicName = "test"
            };

            Assert.Equal(attribute1, attribute2);
        }
        public void HasOneAttribute_Equals_Returns_False_When_Different_Name()
        {
            var attribute1 = new HasOneAttribute
            {
                PublicName = "test"
            };

            var attribute2 = new HasOneAttribute
            {
                PublicName = "test2"
            };

            Assert.NotEqual(attribute1, attribute2);
        }
Exemple #21
0
 /// <summary>
 /// Sets the principal side of a HasOne relationship, which means no
 /// foreign key is involved.
 /// </summary>
 private void SetNavigation(IIdentifiable resource, HasOneAttribute attr, string relatedId,
                            Type relationshipType)
 {
     if (relatedId == null)
     {
         attr.SetValue(resource, null, ResourceFactory);
     }
     else
     {
         var relatedInstance = (IIdentifiable)ResourceFactory.CreateInstance(relationshipType);
         relatedInstance.StringId = relatedId;
         attr.SetValue(resource, relatedInstance, ResourceFactory);
     }
 }
        public void HasManyAttribute_Does_Not_Equal_HasOneAttribute_With_Same_Name()
        {
            RelationshipAttribute attribute1 = new HasManyAttribute
            {
                PublicName = "test"
            };

            RelationshipAttribute attribute2 = new HasOneAttribute
            {
                PublicName = "test"
            };

            Assert.NotEqual(attribute1, attribute2);
            Assert.NotEqual(attribute2, attribute1);
        }
Exemple #23
0
        /// <summary>
        /// Sets the dependent side of a HasOne relationship, which means that a
        /// foreign key also will to be populated.
        /// </summary>
        private void SetForeignKey(IIdentifiable entity, PropertyInfo foreignKey, HasOneAttribute attr, string id)
        {
            bool foreignKeyPropertyIsNullableType = Nullable.GetUnderlyingType(foreignKey.PropertyType) != null ||
                                                    foreignKey.PropertyType == typeof(string);

            if (id == null && !foreignKeyPropertyIsNullableType)
            {
                // this happens when a non-optional relationship is deliberatedly set to null.
                // For a server deserializer, it should be mapped to a BadRequest HTTP error code.
                throw new FormatException($"Cannot set required relationship identifier '{attr.IdentifiablePropertyName}' to null because it is a non-nullable type.");
            }
            var convertedId = TypeHelper.ConvertType(id, foreignKey.PropertyType);

            foreignKey.SetValue(entity, convertedId);
        }
Exemple #24
0
        /// <summary>
        /// Builds a <see cref="ResourceIdentifierObject"/> for a HasOne relationship
        /// </summary>
        private ResourceIdentifierObject GetRelatedResourceLinkage(HasOneAttribute relationship, IIdentifiable entity)
        {
            var relatedEntity = (IIdentifiable)relationship.GetValue(entity);

            if (relatedEntity == null && IsRequiredToOneRelationship(relationship, entity))
            {
                throw new NotSupportedException("Cannot serialize a required to one relationship that is not populated but was included in the set of relationships to be serialized.");
            }

            if (relatedEntity != null)
            {
                return(GetResourceIdentifier(relatedEntity));
            }

            return(null);
        }
        /// <summary>
        /// Sets a HasOne relationship on a parsed resource.
        /// </summary>
        private void SetHasOneRelationship(IIdentifiable resource, HasOneAttribute hasOneRelationship, RelationshipEntry relationshipData)
        {
            if (relationshipData.ManyData != null)
            {
                throw new JsonApiSerializationException("Expected single data element for to-one relationship.",
                                                        $"Expected single data element for '{hasOneRelationship.PublicName}' relationship.");
            }

            var rightResource = CreateRightResource(hasOneRelationship, relationshipData.SingleData);

            hasOneRelationship.SetValue(resource, rightResource);

            // depending on if this base parser is used client-side or server-side,
            // different additional processing per field needs to be executed.
            AfterProcessField(resource, hasOneRelationship, relationshipData);
        }
        private object SetHasOneRelationship(object entity,
                                             PropertyInfo[] entityProperties,
                                             HasOneAttribute attr,
                                             ContextEntity contextEntity,
                                             Dictionary <string, RelationshipData> relationships)
        {
            var relationshipName = attr.PublicRelationshipName;

            if (relationships.TryGetValue(relationshipName, out RelationshipData relationshipData))
            {
                var relationshipAttr = _jsonApiContext.RequestEntity.Relationships
                                       .SingleOrDefault(r => r.PublicRelationshipName == relationshipName);

                if (relationshipAttr == null)
                {
                    throw new JsonApiException(400, $"{_jsonApiContext.RequestEntity.EntityName} does not contain a relationship '{relationshipName}'");
                }

                var rio = (ResourceIdentifierObject)relationshipData.ExposedData;

                var foreignKey     = attr.IdentifiablePropertyName;
                var entityProperty = entityProperties.FirstOrDefault(p => p.Name == foreignKey);
                if (entityProperty == null && rio != null)
                {
                    throw new JsonApiException(400, $"{contextEntity.EntityType.Name} does not contain a foreign key property '{foreignKey}' for has one relationship '{attr.InternalRelationshipName}'");
                }

                if (entityProperty != null)
                {
                    // e.g. PATCH /articles
                    // {... { "relationships":{ "Owner": { "data" :null } } } }
                    if (rio == null && Nullable.GetUnderlyingType(entityProperty.PropertyType) == null)
                    {
                        throw new JsonApiException(400, $"Cannot set required relationship identifier '{attr.IdentifiablePropertyName}' to null.");
                    }

                    var newValue       = rio?.Id ?? null;
                    var convertedValue = TypeHelper.ConvertType(newValue, entityProperty.PropertyType);

                    _jsonApiContext.RelationshipsToUpdate[relationshipAttr] = convertedValue;

                    entityProperty.SetValue(entity, convertedValue);
                }
            }

            return(entity);
        }
Exemple #27
0
        private object SetHasOneRelationship(object entity,
                                             PropertyInfo[] entityProperties,
                                             HasOneAttribute attr,
                                             ContextEntity contextEntity,
                                             Dictionary <string, RelationshipData> relationships,
                                             List <ResourceObject> included = null)
        {
            var relationshipName = attr.PublicRelationshipName;

            if (relationships.TryGetValue(relationshipName, out RelationshipData relationshipData) == false)
            {
                return(entity);
            }

            var rio = (ResourceIdentifierObject)relationshipData.ExposedData;

            var foreignKey         = attr.IdentifiablePropertyName;
            var foreignKeyProperty = entityProperties.FirstOrDefault(p => p.Name == foreignKey);

            if (foreignKeyProperty == null && rio == null)
            {
                return(entity);
            }

            SetHasOneForeignKeyValue(entity, attr, foreignKeyProperty, rio);
            SetHasOneNavigationPropertyValue(entity, attr, rio, included);

            // recursive call ...
            if (included != null)
            {
                var navigationPropertyValue = attr.GetValue(entity);
                var contextGraphEntity      = _jsonApiContext.ContextGraph.GetContextEntity(attr.Type);
                if (navigationPropertyValue != null && contextGraphEntity != null)
                {
                    var includedResource = included.SingleOrDefault(r => r.Type == rio.Type && r.Id == rio.Id);
                    if (includedResource != null)
                    {
                        SetRelationships(navigationPropertyValue, contextGraphEntity, includedResource.Relationships, included);
                    }
                }
            }

            return(entity);
        }
        /// <summary>
        /// Sets the value of the navigation property for the related resource.
        /// If the resource has been included, all attributes will be set.
        /// If the resource has not been included, only the id will be set.
        /// </summary>
        private void SetHasOneNavigationPropertyValue(object entity, HasOneAttribute hasOneAttr, ResourceIdentifierObject rio, List <DocumentData> included)
        {
            // if the resource identifier is null, there should be no reason to instantiate an instance
            if (rio != null && rio.Id != null)
            {
                // we have now set the FK property on the resource, now we need to check to see if the
                // related entity was included in the payload and update its attributes
                var includedRelationshipObject = GetIncludedRelationship(rio, included, hasOneAttr);
                if (includedRelationshipObject != null)
                {
                    hasOneAttr.SetValue(entity, includedRelationshipObject);
                }

                // we need to store the fact that this relationship was included in the payload
                // for EF, the repository will use these pointers to make ensure we don't try to
                // create resources if they already exist, we just need to create the relationship
                _jsonApiContext.HasOneRelationshipPointers.Add(hasOneAttr, includedRelationshipObject);
            }
        }
Exemple #29
0
        private object SetHasOneRelationship(object entity,
                                             PropertyInfo[] entityProperties,
                                             HasOneAttribute attr,
                                             ContextEntity contextEntity,
                                             Dictionary <string, RelationshipData> relationships)
        {
            var relationshipName = attr.PublicRelationshipName;

            if (relationships.TryGetValue(relationshipName, out RelationshipData relationshipData))
            {
                var relationshipAttr = _jsonApiContext.RequestEntity.Relationships
                                       .SingleOrDefault(r => r.PublicRelationshipName == relationshipName);

                if (relationshipAttr == null)
                {
                    throw new JsonApiException(400, $"{_jsonApiContext.RequestEntity.EntityName} does not contain a relationship '{relationshipName}'");
                }

                var rio = (ResourceIdentifierObject)relationshipData.ExposedData;

                if (rio == null)
                {
                    return(entity);
                }

                var newValue = rio.Id;

                var foreignKey     = attr.IdentifiablePropertyName;
                var entityProperty = entityProperties.FirstOrDefault(p => p.Name == foreignKey);
                if (entityProperty == null)
                {
                    throw new JsonApiException(400, $"{contextEntity.EntityType.Name} does not contain a foreign key property '{foreignKey}' for has one relationship '{attr.InternalRelationshipName}'");
                }

                var convertedValue = TypeHelper.ConvertType(newValue, entityProperty.PropertyType);

                _jsonApiContext.RelationshipsToUpdate[relationshipAttr] = convertedValue;

                entityProperty.SetValue(entity, convertedValue);
            }

            return(entity);
        }
        private void SetHasOneForeignKeyValue(object entity, HasOneAttribute hasOneAttr, PropertyInfo foreignKeyProperty, ResourceIdentifierObject rio)
        {
            var foreignKeyPropertyValue = rio?.Id ?? null;

            if (foreignKeyProperty != null)
            {
                // in the case of the HasOne independent side of the relationship, we should still create the shell entity on the other side
                // we should not actually require the resource to have a foreign key (be the dependent side of the relationship)

                // e.g. PATCH /articles
                // {... { "relationships":{ "Owner": { "data": null } } } }
                if (rio == null && Nullable.GetUnderlyingType(foreignKeyProperty.PropertyType) == null)
                {
                    throw new JsonApiException(400, $"Cannot set required relationship identifier '{hasOneAttr.IdentifiablePropertyName}' to null because it is a non-nullable type.");
                }

                var convertedValue = TypeHelper.ConvertType(foreignKeyPropertyValue, foreignKeyProperty.PropertyType);
                foreignKeyProperty.SetValue(entity, convertedValue);
                _jsonApiContext.RelationshipsToUpdate[hasOneAttr] = convertedValue;
            }
        }