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)); }
/// <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); } }
/// <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); }
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}'."); } } }
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); }
/// <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 } }); }
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); }
/// <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); }
/// <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); }
public RelationshipEntry Build(IIdentifiable entity, RelationshipAttribute requestRelationship) { _requestRelationship = requestRelationship; return(GetRelationshipData(requestRelationship, entity)); }
/// <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); }
/// <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) }); }
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); } }
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); } }
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); }
/// <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)); }
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)); }
private IncludeExpression RewriteIncludeForSecondaryEndpoint(IncludeExpression relativeInclude, RelationshipAttribute secondaryRelationship) { var parentElement = relativeInclude != null ? new IncludeElementExpression(secondaryRelationship, relativeInclude.Elements) : new IncludeElementExpression(secondaryRelationship); return(new IncludeExpression(parentElement.AsArray())); }
/// <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 }); }
/// <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); }
/// <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); }
protected virtual Type GetRelationshipType(RelationshipAttribute relation, PropertyInfo prop) => relation is HasOneAttribute ? prop.PropertyType : prop.PropertyType.GetGenericArguments()[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); } }