Example #1
0
 protected override void CreateRelationship(Type entityType, MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships)
 {
     if (RelationshipPredicate(member))
     {
         relationships.Add(new Relationship(member));
     }
 }
 /// <summary>
 /// Registers any member with a RelationshipAttribute as a relationship.
 /// </summary>
 /// <param name="entityType">The entity that is being mapped.</param>
 /// <param name="member">The current member that is being inspected.</param>
 /// <param name="relationshipAtt">A RelationshipAttribute (is null if one does not exist).</param>
 /// <param name="relationships">A list of Relationships.</param>
 protected override void CreateRelationship(Type entityType, MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships)
 {
     if (relationshipAtt != null)
     {
         Relationship relationship = new Relationship(member, relationshipAtt);
         relationships.Add(relationship);
     }
 }
 /// <summary>
 /// Maps a relationship if a RelationshipAttribute is present.
 /// </summary>
 /// <param name="entityType">The entity that is being mapped.</param>
 /// <param name="member">The current member that is being inspected.</param>
 /// <param name="relationshipAtt">A RelationshipAttribute (is null if one does not exist).</param>
 /// <param name="relationships">A list of Relationships.</param>
 protected override void CreateRelationship(Type entityType, MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships)
 {
     if (relationshipAtt != null)
     {
         // Add relationships by RelationshipAttribute
         base.CreateRelationship(entityType, member, relationshipAtt, relationships);
     }
     else
     {
         if (member.MemberType == MemberTypes.Property)
         {
             PropertyInfo propertyInfo = member as PropertyInfo;
             if (typeof(System.Collections.ICollection).IsAssignableFrom(propertyInfo.PropertyType))
             {
                 Relationship relationship = new Relationship(member);
                 relationships.Add(relationship);
             }
         }
     }
 }
        /// <summary>
        /// Responsible for getting the relationship value for a given relationship
        /// attribute of a given resource. It ensures that the relationship value
        /// that it returns is attached to the database without reattaching duplicates instances
        /// to the change tracker. It does so by checking if there already are
        /// instances of the to-be-attached entities in the change tracker.
        /// </summary>
        private object GetTrackedRelationshipValue(RelationshipAttribute relationshipAttr, TResource resource, out bool wasAlreadyAttached)
        {
            wasAlreadyAttached = false;
            if (relationshipAttr is HasOneAttribute hasOneAttr)
            {
                var relationshipValue = (IIdentifiable)hasOneAttr.GetValue(resource);
                if (relationshipValue == null)
                {
                    return(null);
                }
                return(GetTrackedHasOneRelationshipValue(relationshipValue, ref wasAlreadyAttached));
            }

            IEnumerable <IIdentifiable> relationshipValues = (IEnumerable <IIdentifiable>)relationshipAttr.GetValue(resource);

            if (relationshipValues == null)
            {
                return(null);
            }

            return(GetTrackedManyRelationshipValue(relationshipValues, relationshipAttr, ref wasAlreadyAttached));
        }
Example #5
0
        /// <inheritdoc />
        public virtual async Task AddToToManyRelationshipAsync(TId primaryId, ISet<IIdentifiable> secondaryResourceIds, CancellationToken cancellationToken)
        {
            _traceWriter.LogMethodStart(new
            {
                primaryId,
                secondaryResourceIds
            });

            ArgumentGuard.NotNull(secondaryResourceIds, nameof(secondaryResourceIds));

            RelationshipAttribute relationship = _targetedFields.Relationships.Single();

            if (secondaryResourceIds.Any())
            {
                using var collector = new PlaceholderResourceCollector(_resourceFactory, _dbContext);
                TResource primaryResource = collector.CreateForId<TResource, TId>(primaryId);

                await UpdateRelationshipAsync(relationship, primaryResource, secondaryResourceIds, collector, cancellationToken);

                await SaveChangesAsync(cancellationToken);
            }
        }
Example #6
0
        /// <summary>
        /// Builds the values of the relationships object on a resource object.
        /// The server serializer only populates the "data" member when the relationship is included,
        /// and adds links unless these are turned off. This means that if a relationship is not included
        /// and links are turned off, the entry would be completely empty, ie { }, which is not conform
        /// json:api spec. In that case we return null which will omit the entry from the output.
        /// </summary>
        protected override RelationshipEntry GetRelationshipData(RelationshipAttribute relationship, IIdentifiable resource)
        {
            if (relationship == null)
            {
                throw new ArgumentNullException(nameof(relationship));
            }
            if (resource == null)
            {
                throw new ArgumentNullException(nameof(resource));
            }

            RelationshipEntry relationshipEntry = null;
            List <IReadOnlyCollection <RelationshipAttribute> > relationshipChains = null;

            if (Equals(relationship, _requestRelationship) || ShouldInclude(relationship, out relationshipChains))
            {
                relationshipEntry = base.GetRelationshipData(relationship, resource);
                if (relationshipChains != null && relationshipEntry.HasResource)
                {
                    foreach (var chain in relationshipChains)
                    {
                        // traverses (recursively) and extracts all (nested) related resources for the current inclusion chain.
                        _includedBuilder.IncludeRelationshipChain(chain, resource);
                    }
                }
            }

            var links = _linkBuilder.GetRelationshipLinks(relationship, resource);

            if (links != null)
            {
                // if links relationshipLinks should be built for this entry, populate the "links" field.
                (relationshipEntry ??= new RelationshipEntry()).Links = links;
            }

            // if neither "links" nor "data" was populated, return null, which will omit this entry from the output.
            // (see the NullValueHandling settings on <see cref="ResourceObject"/>)
            return(relationshipEntry);
        }
Example #7
0
        private NavigationEntry GetNavigationEntry(TResource resource, RelationshipAttribute relationship)
        {
            EntityEntry <TResource> entityEntry = _dbContext.Entry(resource);

            switch (relationship)
            {
            case HasOneAttribute hasOneRelationship:
            {
                return(entityEntry.Reference(hasOneRelationship.Property.Name));
            }

            case HasManyAttribute hasManyRelationship:
            {
                return(entityEntry.Collection(hasManyRelationship.Property.Name));
            }

            default:
            {
                throw new InvalidOperationException($"Unknown relationship type '{relationship.GetType().Name}'.");
            }
            }
        }
Example #8
0
        private object SetHasOneRelationship(object entity,
                                             PropertyInfo[] entityProperties,
                                             RelationshipAttribute 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);

                var data = (Dictionary <string, string>)relationshipData.ExposedData;

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

                var newValue = data["id"];

                var foreignKey     = attr.InternalRelationshipName + "Id";
                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);
        }
Example #9
0
        /// <inheritdoc />
        public QueryLayer ComposeForGetRelationshipRightIds(RelationshipAttribute relationship, ICollection <IIdentifiable> rightResourceIds)
        {
            ArgumentGuard.NotNull(relationship, nameof(relationship));
            ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds));

            var rightResourceContext = _resourceContextProvider.GetResourceContext(relationship.RightType);
            var rightIdAttribute     = GetIdAttribute(rightResourceContext);

            var typedIds = rightResourceIds.Select(resource => resource.GetTypedId()).ToArray();

            var baseFilter = GetFilter(Array.Empty <QueryExpression>(), rightResourceContext);
            var filter     = CreateFilterByIds(typedIds, rightIdAttribute, baseFilter);

            return(new QueryLayer(rightResourceContext)
            {
                Include = IncludeExpression.Empty,
                Filter = filter,
                Projection = new Dictionary <ResourceFieldAttribute, QueryLayer>
                {
                    [rightIdAttribute] = null
                }
            });
        }
Example #10
0
        private void AddToImplicitlyAffected(IEnumerable includedLefts, RelationshipAttribute relationship, List <IIdentifiable> existingRightResourceList,
                                             Dictionary <RelationshipAttribute, IEnumerable> implicitlyAffected)
        {
            foreach (IIdentifiable ip in includedLefts)
            {
                IList dbRightResourceList = TypeHelper.CreateListFor(relationship.RightType);
                var   relationshipValue   = relationship.GetValue(ip);
                if (!(relationshipValue is IEnumerable))
                {
                    if (relationshipValue != null)
                    {
                        dbRightResourceList.Add(relationshipValue);
                    }
                }
                else
                {
                    AddToList(dbRightResourceList, (IEnumerable)relationshipValue);
                }

                var dbRightResourceListCast = dbRightResourceList.Cast <IIdentifiable>().ToList();
                if (existingRightResourceList != null)
                {
                    dbRightResourceListCast = dbRightResourceListCast.Except(existingRightResourceList, _comparer).ToList();
                }

                if (dbRightResourceListCast.Any())
                {
                    if (!implicitlyAffected.TryGetValue(relationship, out IEnumerable affected))
                    {
                        affected = TypeHelper.CreateListFor(relationship.RightType);
                        implicitlyAffected[relationship] = affected;
                    }

                    AddToList((IList)affected, dbRightResourceListCast);
                }
            }
        }
        /// <summary>
        /// Searches for and parses the included relationship
        /// </summary>
        private IIdentifiable ParseIncludedRelationship(RelationshipAttribute relationshipAttr, ResourceIdentifierObject relatedResourceIdentifier)
        {
            var relatedInstance = (IIdentifiable)_resourceFactory.CreateInstance(relationshipAttr.RightType);

            relatedInstance.StringId = relatedResourceIdentifier.Id;

            var includedResource = GetLinkedResource(relatedResourceIdentifier);

            if (includedResource == null)
            {
                return(relatedInstance);
            }

            var resourceContext = _contextProvider.GetResourceContext(relatedResourceIdentifier.Type);

            if (resourceContext == null)
            {
                throw new InvalidOperationException($"Included type '{relationshipAttr.RightType}' is not a registered json:api resource.");
            }

            SetAttributes(relatedInstance, includedResource.Attributes, resourceContext.Attributes);
            SetRelationships(relatedInstance, includedResource.Relationships, resourceContext.Relationships);
            return(relatedInstance);
        }
Example #12
0
        /// <inheritdoc />
        public virtual async Task SetRelationshipAsync(TResource primaryResource, object secondaryResourceIds, CancellationToken cancellationToken)
        {
            _traceWriter.LogMethodStart(new
            {
                primaryResource,
                secondaryResourceIds
            });

            RelationshipAttribute relationship = _targetedFields.Relationships.Single();

            object secondaryResourceIdsEdited =
                await VisitSetRelationshipAsync(primaryResource, relationship, secondaryResourceIds, OperationKind.SetRelationship, cancellationToken);

            AssertIsNotClearingRequiredRelationship(relationship, primaryResource, secondaryResourceIdsEdited);

            using var collector = new PlaceholderResourceCollector(_resourceFactory, _dbContext);
            await UpdateRelationshipAsync(relationship, primaryResource, secondaryResourceIdsEdited, collector, cancellationToken);

            await _resourceDefinitionAccessor.OnWritingAsync(primaryResource, OperationKind.SetRelationship, cancellationToken);

            await SaveChangesAsync(cancellationToken);

            await _resourceDefinitionAccessor.OnWriteSucceededAsync(primaryResource, OperationKind.SetRelationship, cancellationToken);
        }
        /// <inheritdoc />
        public IReadOnlyCollection <AttrAttribute> GetAttributes(Type resourceType, RelationshipAttribute relationship = null)
        {
            if (resourceType == null)
            {
                throw new ArgumentNullException(nameof(resourceType));
            }

            var sparseFieldSetAttributes = _constraintProviders
                                           .SelectMany(p => p.GetConstraints())
                                           .Where(expressionInScope => relationship == null
                    ? expressionInScope.Scope == null
                    : expressionInScope.Scope != null && expressionInScope.Scope.Fields.Last().Equals(relationship))
                                           .Select(expressionInScope => expressionInScope.Expression)
                                           .OfType <SparseFieldSetExpression>()
                                           .SelectMany(sparseFieldSet => sparseFieldSet.Attributes)
                                           .ToHashSet();

            if (!sparseFieldSetAttributes.Any())
            {
                sparseFieldSetAttributes = GetViewableAttributes(resourceType);
            }

            var inputExpression  = sparseFieldSetAttributes.Any() ? new SparseFieldSetExpression(sparseFieldSetAttributes) : null;
            var outputExpression = _resourceDefinitionAccessor.OnApplySparseFieldSet(resourceType, inputExpression);

            if (outputExpression == null)
            {
                sparseFieldSetAttributes = GetViewableAttributes(resourceType);
            }
            else
            {
                sparseFieldSetAttributes.IntersectWith(outputExpression.Attributes);
            }

            return(sparseFieldSetAttributes);
        }
Example #14
0
        /// <inheritdoc />
        public RelationshipLinks GetRelationshipLinks(RelationshipAttribute relationship, IIdentifiable parent)
        {
            ArgumentGuard.NotNull(relationship, nameof(relationship));
            ArgumentGuard.NotNull(parent, nameof(parent));

            var parentResourceContext = _provider.GetResourceContext(parent.GetType());
            var childNavigation       = relationship.PublicName;
            RelationshipLinks links   = null;

            if (ShouldAddRelationshipLink(parentResourceContext, relationship, LinkTypes.Related))
            {
                links = new RelationshipLinks {
                    Related = GetRelatedRelationshipLink(parentResourceContext.PublicName, parent.StringId, childNavigation)
                };
            }

            if (ShouldAddRelationshipLink(parentResourceContext, relationship, LinkTypes.Self))
            {
                links ??= new RelationshipLinks();
                links.Self = GetSelfRelationshipLink(parentResourceContext.PublicName, parent.StringId, childNavigation);
            }

            return(links);
        }
Example #15
0
 public RelationshipEntry Build(IIdentifiable entity, RelationshipAttribute requestRelationship)
 {
     _requestRelationship = requestRelationship;
     return(GetRelationshipData(requestRelationship, entity));
 }
Example #16
0
        /// <summary>
        /// Inspects the included relationship chains and selects the ones that starts with the specified relationship.
        /// </summary>
        private IReadOnlyCollection <IReadOnlyCollection <RelationshipAttribute> > GetInclusionChainsStartingWith(RelationshipAttribute relationship)
        {
            IncludeExpression include = _evaluatedIncludeCache.Get() ?? IncludeExpression.Empty;
            IReadOnlyCollection <ResourceFieldChainExpression> chains = IncludeChainConverter.GetRelationshipChains(include);

            var inclusionChains = new List <IReadOnlyCollection <RelationshipAttribute> >();

            foreach (ResourceFieldChainExpression chain in chains)
            {
                if (chain.Fields.First().Equals(relationship))
                {
                    inclusionChains.Add(chain.Fields.Cast <RelationshipAttribute>().ToArray());
                }
            }

            return(inclusionChains);
        }
Example #17
0
 /// <summary>
 /// Builds the <see cref="RelationshipEntry"/> entries of the "relationships
 /// objects" The default behaviour is to just construct a resource linkage
 /// with the "data" field populated with "single" or "many" data.
 /// Depending on the requirements of the implementation (server or client serializer),
 /// this may be overridden.
 /// </summary>
 protected virtual RelationshipEntry GetRelationshipData(RelationshipAttribute relationship, IIdentifiable entity)
 {
     return(new RelationshipEntry {
         Data = GetRelatedResourceLinkage(relationship, entity)
     });
 }
 /// <summary>
 /// We only need a empty relationship object entry here. It will be populated in the
 /// ProcessRelationships method.
 /// </summary>
 /// <param name="relationship"></param>
 /// <param name="entity"></param>
 /// <returns></returns>
 protected override RelationshipEntry GetRelationshipData(RelationshipAttribute relationship, IIdentifiable entity)
 {
     return(new RelationshipEntry {
         Links = _linkBuilder.GetRelationshipLinks(relationship, entity)
     });
 }
Example #19
0
 private void AssertRightTypeIsCompatible(ResourceContext rightResourceContext, RelationshipAttribute relationship)
 {
     if (!relationship.RightType.IsAssignableFrom(rightResourceContext.ResourceType))
     {
         throw new JsonApiSerializationException("Relationship contains incompatible resource type.",
                                                 $"Relationship '{relationship.PublicName}' contains incompatible resource type '{rightResourceContext.PublicName}'.",
                                                 atomicOperationIndex: AtomicOperationIndex);
     }
 }
Example #20
0
        private void AssertHasIdOrLid(ResourceIdentifierObject resourceIdentifierObject, RelationshipAttribute relationship)
        {
            if (AtomicOperationIndex != null)
            {
                bool hasNone = resourceIdentifierObject.Id == null && resourceIdentifierObject.Lid == null;
                bool hasBoth = resourceIdentifierObject.Id != null && resourceIdentifierObject.Lid != null;

                if (hasNone || hasBoth)
                {
                    throw new JsonApiSerializationException("Request body must include 'id' or 'lid' element.",
                                                            $"Expected 'id' or 'lid' element in '{relationship.PublicName}' relationship.",
                                                            atomicOperationIndex: AtomicOperationIndex);
                }
            }
            else
            {
                if (resourceIdentifierObject.Id == null)
                {
                    throw new JsonApiSerializationException("Request body must include 'id' element.",
                                                            $"Expected 'id' element in '{relationship.PublicName}' relationship.",
                                                            atomicOperationIndex: AtomicOperationIndex);
                }

                AssertHasNoLid(resourceIdentifierObject);
            }
        }
Example #21
0
 public virtual async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable <string> relationshipIds)
 {
     if (relationship is HasManyThroughAttribute hasManyThrough && parent is IIdentifiable identifiableParent)
     {
         await SetHasManyThroughRelationshipAsync(identifiableParent, hasManyThrough, relationshipIds);
     }
Example #22
0
        /// <summary>
        /// 创建复合导航关系。
        /// </summary>
        /// <param name="source">源数据表。</param>
        /// <param name="target">目标数据表。</param>
        /// <param name="table">所在的表元数据对象。</param>
        /// <param name="attr">关系特性对象。</param>
        /// <returns>复合导航关系。</returns>
        private CompositeNavigateMetadata CreateNavigate(TableMetadata source, TableMetadata target, TableMetadata table, RelationshipAttribute attr)
        {
            var pairs  = CreatePair(table, source, attr.LeftNames, attr.LeftPrincipals);
            var cpairs = CreatePair(table, target, attr.RightNames, attr.RightPrincipals);

            return(new CompositeNavigateMetadata(source, target, table, pairs, cpairs));
        }
Example #23
0
        public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable <string> relationshipIds)
        {
            SetRelationships(parent, relationship, relationshipIds);

            await _context.SaveChangesAsync();
        }
 private bool RequireLoadOfInverseRelationship(RelationshipAttribute relationship, object trackedValueToAssign)
 {
     // See https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/502.
     return(trackedValueToAssign != null && relationship.InverseNavigationProperty != null && IsOneToOneRelationship(relationship));
 }
Example #25
0
        private IncludeExpression RewriteIncludeForSecondaryEndpoint(IncludeExpression relativeInclude, RelationshipAttribute secondaryRelationship)
        {
            var parentElement = relativeInclude != null
                ? new IncludeElementExpression(secondaryRelationship, relativeInclude.Elements)
                : new IncludeElementExpression(secondaryRelationship);

            return(new IncludeExpression(parentElement.AsArray()));
        }
Example #26
0
        /// <inheritdoc />
        public QueryLayer WrapLayerForSecondaryEndpoint <TId>(QueryLayer secondaryLayer, ResourceContext primaryResourceContext, TId primaryId, RelationshipAttribute secondaryRelationship)
        {
            ArgumentGuard.NotNull(secondaryLayer, nameof(secondaryLayer));
            ArgumentGuard.NotNull(primaryResourceContext, nameof(primaryResourceContext));
            ArgumentGuard.NotNull(secondaryRelationship, nameof(secondaryRelationship));

            var innerInclude = secondaryLayer.Include;

            secondaryLayer.Include = null;

            var primaryAttributeSet = _sparseFieldSetCache.GetIdAttributeSetForRelationshipQuery(primaryResourceContext);
            var primaryProjection   = primaryAttributeSet.ToDictionary(key => (ResourceFieldAttribute)key, _ => (QueryLayer)null);

            primaryProjection[secondaryRelationship] = secondaryLayer;

            var primaryFilter      = GetFilter(Array.Empty <QueryExpression>(), primaryResourceContext);
            var primaryIdAttribute = GetIdAttribute(primaryResourceContext);

            return(new QueryLayer(primaryResourceContext)
            {
                Include = RewriteIncludeForSecondaryEndpoint(innerInclude, secondaryRelationship),
                Filter = CreateFilterByIds(primaryId.AsArray(), primaryIdAttribute, primaryFilter),
                Projection = primaryProjection
            });
        }
Example #27
0
 /// <summary>
 /// Maps a relationship if a RelationshipAttribute is present.
 /// </summary>
 /// <param name="entityType">The entity that is being mapped.</param>
 /// <param name="member">The current member that is being inspected.</param>
 /// <param name="relationshipAtt">A RelationshipAttribute (is null if one does not exist).</param>
 /// <param name="relationships">A list of Relationships.</param>
 protected override void CreateRelationship(Type entityType, MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships)
 {
     if (relationshipAtt != null)
     {
         // Add relationships by RelationshipAttribute
         base.CreateRelationship(entityType, member, relationshipAtt, relationships);
     }
     else
     {
         if (member.MemberType == MemberTypes.Property)
         {
             PropertyInfo propertyInfo = member as PropertyInfo;
             if (typeof(ICollection).IsAssignableFrom(propertyInfo.PropertyType))
             {
                 Relationship relationship = new Relationship(member);
                 relationships.Add(relationship);
             }
         }
     }
 }
 /// <summary>
 /// Inspect a member and optionally add a Relationship.
 /// </summary>
 /// <param name="entityType">The entity that is being mapped.</param>
 /// <param name="member">The current member that is being inspected.</param>
 /// <param name="relationshipAtt">A RelationshipAttribute (is null if one does not exist).</param>
 /// <param name="relationships">A list of Relationships.</param>
 protected abstract void CreateRelationship(Type entityType, MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships);
        private IIdentifiable GetIncludedRelationship(ResourceIdentifierObject relatedResourceIdentifier, List <DocumentData> includedResources, RelationshipAttribute relationshipAttr)
        {
            // at this point we can be sure the relationshipAttr.Type is IIdentifiable because we were able to successfully build the ContextGraph
            var relatedInstance = relationshipAttr.Type.New <IIdentifiable>();

            relatedInstance.StringId = relatedResourceIdentifier.Id;

            // can't provide any more data other than the rio since it is not contained in the included section
            if (includedResources == null || includedResources.Count == 0)
            {
                return(relatedInstance);
            }

            var includedResource = GetLinkedResource(relatedResourceIdentifier, includedResources);

            if (includedResource == null)
            {
                return(relatedInstance);
            }

            var contextEntity = _jsonApiContext.ContextGraph.GetContextEntity(relationshipAttr.Type);

            if (contextEntity == null)
            {
                throw new JsonApiException(400, $"Included type '{relationshipAttr.Type}' is not a registered json:api resource.");
            }

            SetEntityAttributes(relatedInstance, contextEntity, includedResource.Attributes);

            return(relatedInstance);
        }
Example #30
0
        /// <summary>
        /// Creates a sharing export (<see cref="ObjectExport"/>) for the current <see cref="GatheredObject.Object"/> and then serializes it as a <see cref="ShareDefinition"/>.
        /// This includes mapping any [<see cref="RelationshipAttribute"/>] properties on the <see cref="GatheredObject.Object"/> to the relevant Share Guid (which must
        /// exist in branchParents).
        ///
        /// <para>ToShareDefinitionWithChildren if you want a full list of shares for the whole tree</para>
        /// </summary>
        /// <param name="shareManager"></param>
        /// <param name="branchParents"></param>
        /// <returns></returns>
        public ShareDefinition ToShareDefinition(ShareManager shareManager, List <ShareDefinition> branchParents)
        {
            var export = shareManager.GetNewOrExistingExportFor(Object);

            Dictionary <string, object> properties = new Dictionary <string, object>();
            Dictionary <RelationshipAttribute, Guid> relationshipProperties = new Dictionary <RelationshipAttribute, Guid>();

            AttributePropertyFinder <RelationshipAttribute> relationshipFinder = new AttributePropertyFinder <RelationshipAttribute>(Object);
            AttributePropertyFinder <NoMappingToDatabase>   noMappingFinder    = new AttributePropertyFinder <NoMappingToDatabase>(Object);


            //for each property in the Object class
            foreach (PropertyInfo property in Object.GetType().GetProperties())
            {
                //if it's the ID column skip it
                if (property.Name == "ID")
                {
                    continue;
                }

                //skip [NoMapping] columns
                if (noMappingFinder.GetAttribute(property) != null)
                {
                    continue;
                }

                //skip IRepositories (these tell you where the object came from)
                if (typeof(IRepository).IsAssignableFrom(property.PropertyType))
                {
                    continue;
                }

                RelationshipAttribute attribute = relationshipFinder.GetAttribute(property);

                //if it's a relationship to a shared object
                if (attribute != null && (attribute.Type == RelationshipType.SharedObject || attribute.Type == RelationshipType.OptionalSharedObject))
                {
                    var  idOfParent   = property.GetValue(Object);
                    Type typeOfParent = attribute.Cref;

                    var parent = branchParents.SingleOrDefault(d => d.Type == typeOfParent && d.ID.Equals(idOfParent));

                    //if the parent is not being shared along with us
                    if (parent == null)
                    {
                        //if a reference is required (i.e. not optional)
                        if (attribute.Type != RelationshipType.OptionalSharedObject)
                        {
                            throw new SharingException("Property " + property + " on Type " + Object.GetType() + " is marked [Relationship] but we found no ShareDefinition amongst the current objects parents to satisfy this property");
                        }
                    }
                    else
                    {
                        relationshipProperties.Add(attribute, parent.SharingGuid);
                    }
                }
                else
                {
                    properties.Add(property.Name, property.GetValue(Object));
                }
            }

            return(new ShareDefinition(export.SharingUIDAsGuid, Object.ID, Object.GetType(), properties, relationshipProperties));
        }
        // helper method used in GetTrackedRelationshipValue. See comments below.
        private IList GetTrackedManyRelationshipValue(IEnumerable <IIdentifiable> relationshipValueList, RelationshipAttribute relationshipAttr, ref bool wasAlreadyAttached)
        {
            if (relationshipValueList == null)
            {
                return(null);
            }
            bool _wasAlreadyAttached      = false;
            var  trackedPointerCollection = relationshipValueList.Select(pointer =>
            {       // convert each element in the value list to relationshipAttr.DependentType.
                var tracked = AttachOrGetTracked(pointer);
                if (tracked != null)
                {
                    _wasAlreadyAttached = true;
                }
                return(Convert.ChangeType(tracked ?? pointer, relationshipAttr.RightType));
            })
                                            .ToList()
                                            .Cast(relationshipAttr.RightType);

            if (_wasAlreadyAttached)
            {
                wasAlreadyAttached = true;
            }
            return((IList)trackedPointerCollection);
        }
 /// <summary>
 /// Registers any member with a RelationshipAttribute as a relationship.
 /// </summary>
 /// <param name="entityType">The entity that is being mapped.</param>
 /// <param name="member">The current member that is being inspected.</param>
 /// <param name="relationshipAtt">A RelationshipAttribute (is null if one does not exist).</param>
 /// <param name="relationships">A list of Relationships.</param>
 protected override void CreateRelationship(Type entityType, MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships)
 {
     if (relationshipAtt != null)
     {
         Relationship relationship = new Relationship(member, relationshipAtt);
         relationships.Add(relationship);
     }
 }
 protected virtual Type GetRelationshipType(RelationshipAttribute relation, PropertyInfo prop) =>
 relation is HasOneAttribute ? prop.PropertyType : prop.PropertyType.GetGenericArguments()[0];
Example #34
0
        private void AssertHasType(ResourceIdentifierObject resourceIdentifierObject, RelationshipAttribute relationship)
        {
            if (resourceIdentifierObject.Type == null)
            {
                var details = relationship != null
                    ? $"Expected 'type' element in '{relationship.PublicName}' relationship."
                    : "Expected 'type' element in 'data' element.";

                throw new JsonApiSerializationException("Request body must include 'type' element.", details,
                                                        atomicOperationIndex: AtomicOperationIndex);
            }
        }