protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved) { if (base.RequestDescription.LinkUri) { bool needPop = base.PushSegmentForRoot(); this.WriteLinkCollection(elements, hasMoved); base.PopSegmentName(needPop); } else { this.collectionWriter = this.writer.CreateODataCollectionWriter(); ODataCollectionStart collectionStart = new ODataCollectionStart { Name = this.ComputeContainerName() }; this.collectionWriter.WriteStart(collectionStart); while (hasMoved) { object current = elements.Current; ResourceType propertyResourceType = (current == null) ? base.RequestDescription.TargetResourceType : WebUtil.GetResourceType(base.Provider, current); if (propertyResourceType == null) { throw new InvalidOperationException(System.Data.Services.Strings.Serializer_UnsupportedTopLevelType(current.GetType())); } this.collectionWriter.WriteItem(base.GetPropertyValue("element", propertyResourceType, current, false)); hasMoved = elements.MoveNext(); } this.collectionWriter.WriteEnd(); this.collectionWriter.Flush(); } }
protected override void WriteTopLevelElement(IExpandedResult expandedResult, object element) { string propertyName = this.ComputeContainerName(); if (base.RequestDescription.LinkUri) { bool needPop = base.PushSegmentForRoot(); this.WriteLink(element); base.PopSegmentName(needPop); } else { ResourceType type; if (element == null) { type = (base.RequestDescription.TargetKind == RequestTargetKind.OpenProperty) ? ResourceType.PrimitiveStringResourceType : base.RequestDescription.TargetResourceType; } else { type = (base.RequestDescription.TargetKind == RequestTargetKind.Collection) ? base.RequestDescription.TargetResourceType : WebUtil.GetResourceType(base.Provider, element); } if (type == null) { throw new InvalidOperationException(System.Data.Services.Strings.Serializer_UnsupportedTopLevelType(element.GetType())); } ODataProperty property = new ODataProperty { Name = propertyName, Value = base.GetPropertyValue(propertyName, type, element, false) }; this.writer.WriteProperty(property); } }
/// <summary> /// Return the collection of links as ODataEntityReferenceLink instances /// </summary> /// <param name="elements">Elements whose uri need to be written out.</param> /// <param name="linksCollection">LinkCollection instance which represents the collection getting written out.</param> /// <returns>Return the collection of links as ODataEntityReferenceLink instances.</returns> private IEnumerable <ODataEntityReferenceLink> GetLinksCollection(QueryResultInfo elements, ODataEntityReferenceLinks linksCollection) { object lastObject = null; IExpandedResult lastExpandedSkipToken = null; while (elements.HasMoved) { object element = elements.Current; IExpandedResult skipToken = null; if (element != null) { IExpandedResult expanded = element as IExpandedResult; if (expanded != null) { element = GetExpandedElement(expanded); skipToken = this.GetSkipToken(expanded); } } this.IncrementSegmentResultCount(); yield return(new ODataEntityReferenceLink { Url = this.GetEntityEditLink(element) }); elements.MoveNext(); lastObject = element; lastExpandedSkipToken = skipToken; } if (this.NeedNextPageLink(elements)) { linksCollection.NextPageLink = this.GetNextLinkUri(lastObject, lastExpandedSkipToken, this.RequestDescription.ResultUri); yield break; } }
private IEnumerable <ODataEntityReferenceLink> GetLinksCollection(IEnumerator elements, bool hasMoved, ODataEntityReferenceLinks linksCollection) { object lastObject = null; IExpandedResult skipTokenExpandedResult = null; Label_PostSwitchInIterator :; if (hasMoved) { object current = elements.Current; IExpandedResult skipToken = null; if (current != null) { IExpandedResult expanded = current as IExpandedResult; if (expanded != null) { current = Serializer.GetExpandedElement(expanded); skipToken = this.GetSkipToken(expanded); } } this.IncrementSegmentResultCount(); ODataEntityReferenceLink iteratorVariable4 = new ODataEntityReferenceLink { Url = this.GetEntityUri(current) }; yield return(iteratorVariable4); hasMoved = elements.MoveNext(); lastObject = current; skipTokenExpandedResult = skipToken; goto Label_PostSwitchInIterator; } if (this.NeedNextPageLink(elements)) { linksCollection.NextPageLink = this.GetNextLinkUri(lastObject, skipTokenExpandedResult, this.RequestDescription.ResultUri); } }
/// <summary>Writes a single top-level element.</summary> /// <param name="expandedResult">Expandd results on the specified <paramref name="element"/>.</param> /// <param name="element">Element to write, possibly null.</param> protected override void WriteTopLevelElement(IExpandedResult expandedResult, object element) { Debug.Assert( element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty, "element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty"); Debug.Assert( this.RequestDescription.IsSingleResult, "this.RequestDescription.IsSingleResult -- primitive collections not currently supported"); string propertyName = this.RequestDescription.ContainerName; ResourceType resourceType; if (element == null) { if (this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty) { resourceType = ResourceType.PrimitiveStringResourceType; } else { resourceType = this.RequestDescription.TargetResourceType; } } else { resourceType = WebUtil.GetResourceType(this.Provider, element); } if (resourceType == null) { throw new InvalidOperationException(Strings.Serializer_UnsupportedTopLevelType(element.GetType())); } this.WriteValueWithName(element, propertyName, resourceType); }
/// <summary>Writes multiple top-level elements, possibly none.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="elements">Enumerator for elements to write.</param> protected override void WriteTopLevelElements(IExpandedResult expanded, QueryResultInfo elements) { Debug.Assert(elements != null, "elements != null"); Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult"); string title; if (this.RequestDescription.TargetKind != RequestTargetKind.OpenProperty && this.RequestDescription.TargetSource == RequestTargetSource.Property) { title = this.RequestDescription.Property.Name; } else { title = this.RequestDescription.ContainerName; } this.dataServicesODataWriter = this.CreateODataWriter(true /*forFeed*/); bool needPop = this.PushSegmentForRoot(); this.WriteFeedElements( expanded, elements, this.RequestDescription.TargetResourceType.ElementType(), title, // title () => new Uri(this.RequestDescription.LastSegmentInfo.Identifier, UriKind.Relative), () => this.RequestDescription.ResultUri, // absoluteUri true); this.PopSegmentName(needPop); }
/// <summary>Writes a single open property of a resource or complex object.</summary> /// <param name="propertyName">The name of the property to write.</param> /// <param name="propertyValue">The value of the property to write.</param> /// <param name="parentUri">Uri of the object whose properties are getting written.</param> private void WriteObjectOpenProperty(string propertyName, object propertyValue, Uri parentUri) { IExpandedResult expandedValue = propertyValue as IExpandedResult; if (expandedValue != null) { propertyValue = GetExpandedElement(expandedValue); } ResourceType propertyResourceType; if (propertyValue == null || propertyValue == DBNull.Value) { propertyValue = null; propertyResourceType = ResourceType.PrimitiveStringResourceType; } else { // We need to ensure that strings and other primitive types which implement IEnumerable // are not considered as containing elements. propertyResourceType = WebUtil.GetResourceType(this.Provider, propertyValue); if (propertyResourceType == null) { // Black-listed types are not supported. return; } } this.writer.WriteName(propertyName); bool needPop = this.PushSegmentForOpenProperty(propertyName, propertyResourceType); this.WriteElementWithName(expandedValue, propertyValue, propertyName, Serializer.AppendEntryToUri(parentUri, propertyName), false /*topLevel*/); this.PopSegmentName(needPop); }
/// <summary> /// Write out the uri for the given elements. /// </summary> /// <param name="elements">elements whose uri need to be writtne out</param> /// <param name="hasMoved">the current state of the enumerator.</param> /// <remarks>This method accounts for each link as a written entity on the current segment.</remarks> protected override void WriteLinkCollection(IEnumerator elements, bool hasMoved) { this.writer.StartObjectScope(); this.writer.WriteDataWrapper(); if (this.JsonFormatVersion >= 2) { // see comments in WriteTopLevelElements this.writer.StartObjectScope(); this.writer.WriteDataArrayName(); } this.writer.StartArrayScope(); object lastObject = null; IExpandedResult lastExpandedSkipToken = null; while (hasMoved) { object o = elements.Current; IExpandedResult skipToken = null; if (o != null) { IExpandedResult expanded = o as IExpandedResult; if (expanded != null) { o = GetExpandedElement(expanded); skipToken = this.GetSkipToken(expanded); } Uri uri = Serializer.GetUri(o, this.Provider, this.CurrentContainer, this.AbsoluteServiceUri); this.WriteLinkObject(uri); } hasMoved = elements.MoveNext(); lastObject = o; lastExpandedSkipToken = skipToken; } this.writer.EndScope(); if (this.JsonFormatVersion >= 2) { // $count=inline support if (this.RequestDescription.CountOption == RequestQueryCountOption.Inline) { this.WriteRowCount(); } if (this.NeedNextPageLink(elements)) { this.WriteNextPageLink(lastObject, lastExpandedSkipToken, this.RequestDescription.ResultUri); } this.writer.EndScope(); } this.writer.EndScope(); }
protected IExpandedResult GetSkipToken(IExpandedResult expanded) { if (((expanded != null) && !this.IsCustomPaged) && !this.RequestDescription.IsRequestForEnumServiceOperation) { return(expanded.GetExpandedPropertyValue("$skiptoken") as IExpandedResult); } return(null); }
/// <summary>Writes all the properties of the specified resource.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="resource">Resource with properties to write out.</param> /// <param name="resourceType">Type for the specified resource (saves the lookup in this method).</param> /// <param name="uri">uri of the resource whose properties are getting written</param> private void WriteResourceProperties(IExpandedResult expanded, object resource, ResourceType resourceType, Uri uri) { Debug.Assert(resource != null, "resource != null"); Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resource must be entity type"); Debug.Assert(uri != null, "uri != null"); this.WriteObjectProperties(expanded, resource, resourceType, uri, true); }
/// <summary>Writes a single top-level element.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="element">Element to write, possibly null.</param> protected override void WriteTopLevelElement(IExpandedResult expanded, object element) { Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult"); bool needPop = this.PushSegmentForRoot(); this.writer.StartObjectScope(); // { this.writer.WriteDataWrapper(); // "d" : this.WriteElementWithName(expanded, element, this.RequestDescription.ContainerName, this.RequestDescription.ResultUri, true /*topLevel*/); this.writer.EndScope(); // } this.PopSegmentName(needPop); }
private void WriteEntry(IExpandedResult expanded, object element, bool resourceInstanceInFeed, ResourceType expectedType) { Uri uri; Func <ProjectionNode, bool> predicate = null; base.IncrementSegmentResultCount(); ODataEntry entry = new ODataEntry(); AtomEntryMetadata annotation = new AtomEntryMetadata(); entry.SetAnnotation <AtomEntryMetadata>(annotation); string name = expectedType.Name; ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(base.Provider, element); if (actualResourceType.ResourceTypeKind != ResourceTypeKind.EntityType) { throw new DataServiceException(500, System.Data.Services.Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(actualResourceType.FullName)); } Uri absoluteUri = Serializer.GetIdAndEditLink(element, actualResourceType, base.Provider, base.CurrentContainer, base.AbsoluteServiceUri, out uri); Uri relativeUri = new Uri(absoluteUri.AbsoluteUri.Substring(base.AbsoluteServiceUri.AbsoluteUri.Length), UriKind.Relative); entry.MediaResource = this.GetMediaResource(element, actualResourceType, name, relativeUri); entry.TypeName = actualResourceType.FullName; entry.Id = uri.AbsoluteUri; entry.EditLink = relativeUri; AtomLinkMetadata metadata2 = new AtomLinkMetadata { Title = name }; annotation.EditLink = metadata2; string eTagValue = base.GetETagValue(element, actualResourceType); if (eTagValue != null) { entry.ETag = eTagValue; } IEnumerable <ProjectionNode> projections = base.GetProjections(); if (projections != null) { if (predicate == null) { predicate = projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(actualResourceType); } projections = projections.Where <ProjectionNode>(predicate); entry.SetAnnotation <ProjectedPropertiesAnnotation>(new ProjectedPropertiesAnnotation(from p in projections select p.PropertyName)); } entry.AssociationLinks = this.GetEntityAssociationLinks(actualResourceType, relativeUri, projections); this.PopulateODataOperations(element, resourceInstanceInFeed, entry, actualResourceType); this.odataWriter.WriteStart(entry); this.WriteNavigationProperties(expanded, element, resourceInstanceInFeed, actualResourceType, absoluteUri, relativeUri, projections); entry.Properties = this.GetEntityProperties(element, actualResourceType, relativeUri, projections); this.odataWriter.WriteEnd(); }
protected ResourcePropertyInfo GetNavigationPropertyInfo(IExpandedResult expanded, object customObject, ResourceType currentResourceType, ResourceProperty property) { ExpandedProjectionNode node; object obj2 = null; bool expand = this.ShouldExpandSegment(property, currentResourceType, out node); if (expand) { obj2 = this.GetExpandedProperty(expanded, customObject, property, node); } return(ResourcePropertyInfo.CreateResourcePropertyInfo(property, obj2, node, expand)); }
/// <summary>Writes multiple top-level elements, possibly none.</summary> /// <param name="expanded">Expanded results for elements.</param> /// <param name="elements">Enumerator for elements to write.</param> protected override void WriteTopLevelElements(IExpandedResult expanded, QueryResultInfo elements) { Debug.Assert( !this.RequestDescription.IsSingleResult, "!this.RequestDescription.IsSingleResult -- otherwise WriteTopLevelElement should have been called"); if (this.RequestDescription.LinkUri) { bool needPop = this.PushSegmentForRoot(); this.WriteLinkCollection(elements); this.PopSegmentName(needPop); } else { MetadataProviderEdmModel model = this.Service.Provider.GetMetadataProviderEdmModel(); OperationWrapper operation = this.RequestDescription.LastSegmentInfo.Operation; IEdmOperation edmOperation = model.GetRelatedOperation(operation); Debug.Assert(edmOperation != null, "edmOperation != null"); IEdmCollectionTypeReference collectionType = (IEdmCollectionTypeReference)edmOperation.ReturnType; bool isJsonLightResponse = ContentTypeUtil.IsResponseMediaTypeJsonLight(this.Service, /*isEntryOrFeed*/ false); this.collectionWriter = this.writer.CreateODataCollectionWriter(isJsonLightResponse ? null : collectionType.ElementType()); ODataCollectionStart collectionStart = new ODataCollectionStart { Name = this.ComputeContainerName() }; collectionStart.SetSerializationInfo(new ODataCollectionStartSerializationInfo { CollectionTypeName = collectionType.FullName() }); this.collectionWriter.WriteStart(collectionStart); while (elements.HasMoved) { object element = elements.Current; ResourceType resourceType = element == null ? this.RequestDescription.TargetResourceType : WebUtil.GetResourceType(this.Provider, element); if (resourceType == null) { throw new InvalidOperationException(Microsoft.OData.Service.Strings.Serializer_UnsupportedTopLevelType(element.GetType())); } this.collectionWriter.WriteItem(this.GetPropertyValue(XmlConstants.XmlCollectionItemElementName, resourceType, element, false /*openProperty*/).FromODataValue()); elements.MoveNext(); } this.collectionWriter.WriteEnd(); this.collectionWriter.Flush(); } }
protected object GetExpandedProperty(IExpandedResult expanded, object customObject, ResourceProperty property, ExpandedProjectionNode expandedNode) { if (expanded == null) { return(WebUtil.GetPropertyValue(this.Provider, customObject, null, property, null)); } string name = property.Name; if ((expandedNode != null) && (this.GetCurrentExpandedProjectionNode().ResourceType != expandedNode.TargetResourceType)) { name = expandedNode.TargetResourceType.FullName + "/" + property.Name; } return(expanded.GetExpandedPropertyValue(name)); }
/// <summary>Writes a single top-level element.</summary> /// <param name="expandedResult">Expandd results on the specified <paramref name="element"/>.</param> /// <param name="element">Element to write, possibly null.</param> protected override void WriteTopLevelElement(IExpandedResult expandedResult, object element) { Debug.Assert( element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty, "element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty"); Debug.Assert( this.RequestDescription.IsSingleResult, "this.RequestDescription.IsSingleResult -- primitive collections not currently supported"); string propertyName = this.ComputeContainerName(); if (this.RequestDescription.LinkUri) { bool needPop = this.PushSegmentForRoot(); this.WriteLink(element); this.PopSegmentName(needPop); } else { ResourceType resourceType; if (element == null) { resourceType = this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty ? ResourceType.PrimitiveStringResourceType : this.RequestDescription.TargetResourceType; } else { resourceType = this.RequestDescription.TargetKind == RequestTargetKind.Collection ? this.RequestDescription.TargetResourceType : WebUtil.GetResourceType(this.Provider, element); } if (resourceType == null) { throw new InvalidOperationException(Microsoft.OData.Service.Strings.Serializer_UnsupportedTopLevelType(element.GetType())); } var odataProperty = new ODataProperty { Name = propertyName, Value = this.GetPropertyValue(propertyName, resourceType, element, this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty /*openProperty*/) }; this.writer.WriteProperty(odataProperty); } }
internal void WriteRequest(IEnumerator queryResults, bool hasMoved) { IExpandedResult expanded = queryResults as IExpandedResult; if (this.requestDescription.IsSingleResult) { this.WriteTopLevelElement(expanded, queryResults.Current); if (queryResults.MoveNext()) { throw new InvalidOperationException(System.Data.Services.Strings.SingleResourceExpected); } } else { this.WriteTopLevelElements(expanded, queryResults, hasMoved); } }
protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved) { string name; if ((base.RequestDescription.TargetKind != RequestTargetKind.OpenProperty) && (base.RequestDescription.TargetSource == RequestTargetSource.Property)) { name = base.RequestDescription.Property.Name; } else { name = base.RequestDescription.ContainerName; } this.odataWriter = this.messageWriter.CreateODataFeedWriter(); bool needPop = base.PushSegmentForRoot(); this.WriteFeedElements(expanded, elements, base.RequestDescription.TargetResourceType.ElementType(), name, new Uri(base.RequestDescription.LastSegmentInfo.Identifier, UriKind.Relative), base.RequestDescription.ResultUri, hasMoved, true); base.PopSegmentName(needPop); }
protected override void WriteTopLevelElement(IExpandedResult expanded, object element) { ResourceType targetResourceType; this.odataWriter = this.messageWriter.CreateODataEntryWriter(); if ((base.RequestDescription.TargetSource == RequestTargetSource.EntitySet) || (base.RequestDescription.TargetSource == RequestTargetSource.ServiceOperation)) { targetResourceType = base.RequestDescription.TargetResourceType; } else { targetResourceType = base.RequestDescription.Property.ResourceType; } bool needPop = base.PushSegmentForRoot(); this.WriteEntry(expanded, element, false, targetResourceType); base.PopSegmentName(needPop); }
/// <summary>Writes a single declared property of the specified resource or complex object</summary> /// <param name="expanded">Expanded properties for the result/</param> /// <param name="customObject">Resource or complex object with property to write out.</param> /// <param name="property">The resource property to write out.</param> /// <param name="parentUri">Uri of the object whose properties are getting written.</param> private void WriteObjectDeclaredProperty(IExpandedResult expanded, object customObject, ResourceProperty property, Uri parentUri) { string propertyName = property.Name; this.writer.WriteName(propertyName); // For any navigation property, we just stick the deferred element with the uri // This uri is different from the canonical uri: we just append the property name // to the parent uri. We don't want to analyze the nav property value in either case bool mayDefer = property.TypeKind == ResourceTypeKind.EntityType; if (mayDefer && !this.ShouldExpandSegment(propertyName)) { this.WriteDeferredContentElement(Serializer.AppendEntryToUri(parentUri, propertyName)); } else { object propertyValue; IExpandedResult expandedValue = null; if (mayDefer) { propertyValue = GetExpandedProperty(this.Provider, expanded, customObject, property); expandedValue = propertyValue as IExpandedResult; if (expandedValue != null) { propertyValue = GetExpandedElement(expandedValue); } } else { propertyValue = GetExpandedProperty(this.Provider, null, customObject, property); } if (propertyValue == null || propertyValue == DBNull.Value) { propertyValue = null; } bool needPop = this.PushSegmentForProperty(property); this.WriteElementWithName(expandedValue, propertyValue, propertyName, Serializer.AppendEntryToUri(parentUri, propertyName), false /*topLevel*/); this.PopSegmentName(needPop); } }
/// <summary> /// Write out the uri for the given elements /// </summary> /// <param name="elements">elements whose uri need to be writtne out</param> /// <param name="hasMoved">the current state of the enumerator.</param> protected override void WriteLinkCollection(IEnumerator elements, bool hasMoved) { this.writer.WriteStartElement(XmlConstants.LinkCollectionElementName, XmlConstants.DataWebNamespace); // write count? if (this.RequestDescription.CountOption == RequestQueryCountOption.Inline) { this.WriteRowCount(); } object lastObject = null; IExpandedResult lastExpandedSkipToken = null; while (hasMoved) { object element = elements.Current; IExpandedResult skipToken = null; if (element != null) { IExpandedResult expanded = element as IExpandedResult; if (expanded != null) { element = GetExpandedElement(expanded); skipToken = this.GetSkipToken(expanded); } } this.WriteLink(element); hasMoved = elements.MoveNext(); lastObject = element; lastExpandedSkipToken = skipToken; } if (this.NeedNextPageLink(elements)) { this.WriteNextPageLink(lastObject, lastExpandedSkipToken, this.RequestDescription.ResultUri); } this.writer.WriteEndElement(); }
public object[] GetSkipToken(IDataServiceQueryProvider dataServiceQueryPovider) { if (this.lastObject != null) { if (this.lastObject.GetType() == typeof(int)) { return(new object[] { -1 }); } object resource = this.lastObject; IExpandedResult expandedResult = resource as IExpandedResult; if (expandedResult != null) { resource = expandedResult.ExpandedElement; } IEnumerable <string> keyPropertyNames = this.IsReflectableResourceType ? new string[] { "ID" } : this.resourceType.KeyProperties.Select(property => property.Name); ProjectedWrapper projectedWrapper = resource as ProjectedWrapper; if (projectedWrapper != null) { return(keyPropertyNames.Select(propertyName => projectedWrapper.GetProjectedPropertyValue(propertyName)).ToArray()); } else if (dataServiceQueryPovider != null) { return(keyPropertyNames.Select(propertyName => dataServiceQueryPovider.GetPropertyValue(resource, this.resourceType.Properties.Single(p => p.Name == propertyName))).ToArray()); } else { return(keyPropertyNames.Select(propertyName => resource.GetType().GetProperty(propertyName).GetValue(resource, null)).ToArray()); } } return(new Random().Next() % 2 == 0 ? null : new object[0]); }
/// <summary>Writes multiple top-level elements, possibly none.</summary> /// <param name="expanded">Expanded results for elements.</param> /// <param name="elements">Enumerator for elements to write.</param> /// <param name="hasMoved">Whether <paramref name="elements"/> was succesfully advanced to the first element.</param> protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved) { Debug.Assert( !this.RequestDescription.IsSingleResult, "!this.RequestDescription.IsSingleResult -- otherwise WriteTopLevelElement should have been called"); this.writer.WriteStartElement(this.RequestDescription.ContainerName, XmlConstants.DataWebNamespace); while (hasMoved) { object element = elements.Current; ResourceType resourceType = element == null ? this.RequestDescription.TargetResourceType : WebUtil.GetResourceType(this.Provider, element); if (resourceType == null) { throw new InvalidOperationException(Strings.Serializer_UnsupportedTopLevelType(element.GetType())); } this.WriteValueWithName(element, XmlConstants.XmlCollectionItemElementName, resourceType); hasMoved = elements.MoveNext(); } this.writer.WriteEndElement(); }
protected Uri GetNextLinkUri(object lastObject, IExpandedResult skipTokenExpandedResult, Uri absoluteUri) { SkipTokenBuilder builder2; UriBuilder builder = new UriBuilder(absoluteUri); if (this.IsRootContainer) { if (!this.IsCustomPaged) { if (skipTokenExpandedResult != null) { builder2 = new SkipTokenBuilderFromExpandedResult(skipTokenExpandedResult, this.RequestDescription.SkipTokenExpressionCount); } else { builder2 = new SkipTokenBuilderFromProperties(lastObject, this.Provider, this.RequestDescription.SkipTokenProperties); } } else { builder2 = new SkipTokenBuilderFromCustomPaging(this.currentSkipTokenForCustomPaging); } builder.Query = this.GetNextPageQueryParametersForRootContainer().Append(builder2.GetSkipToken()).ToString(); } else { if (!this.IsCustomPaged) { builder2 = new SkipTokenBuilderFromProperties(lastObject, this.Provider, this.CurrentContainer.ResourceType.KeyProperties); } else { builder2 = new SkipTokenBuilderFromCustomPaging(this.currentSkipTokenForCustomPaging); } builder.Query = this.GetNextPageQueryParametersForExpandedContainer().Append(builder2.GetSkipToken()).ToString(); } return(builder.Uri); }
/// <summary>Writes a single top-level element.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="element">Element to write, possibly null.</param> protected override void WriteTopLevelElement(IExpandedResult expanded, object element) { Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult"); Debug.Assert(this.RequestDescription.TargetKind == RequestTargetKind.Resource, "this.RequestDescription.TargetKind == RequestTargetKind.Resource"); Debug.Assert(element != null, "element != null"); this.dataServicesODataWriter = this.CreateODataWriter(false /*forFeed*/); ResourceType targetResourceType; if (this.RequestDescription.TargetSource == RequestTargetSource.EntitySet || this.RequestDescription.TargetSource == RequestTargetSource.ServiceOperation) { targetResourceType = this.RequestDescription.TargetResourceType; } else { Debug.Assert( this.RequestDescription.TargetSource == RequestTargetSource.Property, "TargetSource(" + this.RequestDescription.TargetSource + ") == Property -- otherwise control shouldn't be here."); Debug.Assert(this.RequestDescription.Property != null, "this.RequestDescription.Property - otherwise Property source set with no Property specified."); Debug.Assert( this.RequestDescription.Property.TypeKind == ResourceTypeKind.EntityType, "SyndicationSerializer.WriteTopLevelElement should only be called for serializing out entity types"); targetResourceType = this.RequestDescription.Property.ResourceType; } bool needPop = this.PushSegmentForRoot(); this.WriteEntry( expanded, // expanded element, // element false, // resourceInstanceInFeed targetResourceType); // expectedType this.PopSegmentName(needPop); }
/// <summary> /// Obtains the URI for the link for next page in string format /// </summary> /// <param name="lastObject">Last object serialized to be used for generating $skiptoken</param> /// <param name="skipTokenExpandedResult">The <see cref="IExpandedResult"/> of the $skiptoken property of object corresponding to last serialized object</param> /// <param name="absoluteUri">Absolute response URI</param> /// <returns>URI for the link for next page</returns> protected String GetNextLinkUri(object lastObject, IExpandedResult skipTokenExpandedResult, Uri absoluteUri) { UriBuilder builder = new UriBuilder(absoluteUri); SkipTokenBuilder skipTokenBuilder = null; if (this.IsRootContainer) { if (!this.IsCustomPaged) { if (skipTokenExpandedResult != null) { skipTokenBuilder = new SkipTokenBuilderFromExpandedResult(skipTokenExpandedResult, this.RequestDescription.SkipTokenExpressionCount); } else { Debug.Assert(this.RequestDescription.SkipTokenProperties != null, "Must have skip token properties collection"); Debug.Assert(this.RequestDescription.SkipTokenProperties.Count > 0, "Must have some valid ordered properties in the skip token properties collection"); skipTokenBuilder = new SkipTokenBuilderFromProperties(lastObject, this.Provider, this.RequestDescription.SkipTokenProperties); } } else { Debug.Assert(this.currentSkipTokenForCustomPaging != null, "Must have obtained the skip token for custom paging."); skipTokenBuilder = new SkipTokenBuilderFromCustomPaging(this.currentSkipTokenForCustomPaging); } builder.Query = this.GetNextPageQueryParametersForRootContainer().Append(skipTokenBuilder.GetSkipToken()).ToString(); } else { if (!this.IsCustomPaged) { // Internal results skipTokenBuilder = new SkipTokenBuilderFromProperties(lastObject, this.Provider, this.CurrentContainer.ResourceType.KeyProperties); } else { Debug.Assert(this.currentSkipTokenForCustomPaging != null, "Must have obtained the skip token for custom paging."); skipTokenBuilder = new SkipTokenBuilderFromCustomPaging(this.currentSkipTokenForCustomPaging); } builder.Query = this.GetNextPageQueryParametersForExpandedContainer().Append(skipTokenBuilder.GetSkipToken()).ToString(); } return builder.Uri.AbsoluteUri; }
/// <summary>Write the entry element.</summary> /// <param name="expanded">Expanded result provider for the specified <paramref name="element"/>.</param> /// <param name="element">element representing the entry element</param> /// <param name="expectedType">expected type of the entry element</param> /// <param name="absoluteUri">absolute uri for the entry element</param> /// <param name="relativeUri">relative uri for the entry element</param> /// <param name="target">Target to write to.</param> private void WriteEntryElement(IExpandedResult expanded, object element, ResourceType expectedType, Uri absoluteUri, string relativeUri, SyndicationItem target) { Debug.Assert(element != null || (absoluteUri != null && !String.IsNullOrEmpty(relativeUri)), "Uri's must be specified for null values"); Debug.Assert(target != null, "target != null"); this.IncrementSegmentResultCount(); string title, fullName; if (expectedType == null) { // If the request uri is targetting some open type properties, then we don't know the type of the resource // Hence we assume it to be of object type. The reason we do this is that if the value is null, there is // no way to know what the type of the property would be, and then we write it out as object. If the value // is not null, then we do get the resource type from the instance and write out the actual resource type. title = typeof(object).Name; fullName = typeof(object).FullName; } else { title = expectedType.Name; fullName = expectedType.FullName; } target.Title = new TextSyndicationContent(String.Empty); if (element == null) { SetEntryTypeName(target, fullName); target.AttributeExtensions[QualifiedNullAttribute] = XmlConstants.XmlTrueLiteral; this.WriteOtherElements( element, expectedType, title, absoluteUri, relativeUri, null, target); // Don't know when we hit this code path, keeping existing behaviour in this case target.Authors.Add(EmptyPerson); } else { absoluteUri = Serializer.GetUri(element, this.Provider, this.CurrentContainer, this.AbsoluteServiceUri); Debug.Assert(absoluteUri.AbsoluteUri.StartsWith(this.AbsoluteServiceUri.AbsoluteUri, StringComparison.Ordinal), "absoluteUri.AbsoluteUri.StartsWith(this.AbsoluteServiceUri.AbsoluteUri, StringComparison.Ordinal))"); relativeUri = absoluteUri.AbsoluteUri.Substring(this.AbsoluteServiceUri.AbsoluteUri.Length); ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element); string mediaETag = null; Uri readStreamUri = null; string mediaContentType = null; if (actualResourceType.IsMediaLinkEntry) { this.Service.StreamProvider.GetStreamDescription(element, this.Service.OperationContext, relativeUri, out mediaETag, out readStreamUri, out mediaContentType); } SetEntryTypeName(target, actualResourceType.FullName); this.WriteOtherElements( element, actualResourceType, title, absoluteUri, relativeUri, mediaETag, target); // Write the etag property, if the type has etag properties string etag = this.GetETagValue(element); if (etag != null) { target.AttributeExtensions[new XmlQualifiedName(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace)] = etag; } DictionaryContent content = new DictionaryContent(actualResourceType.Properties.Count); using (EpmContentSerializer epmSerializer = new EpmContentSerializer(actualResourceType, element, target, this.Provider)) { this.WriteObjectProperties(expanded, element, actualResourceType, absoluteUri, relativeUri, target, content, actualResourceType.HasEntityPropertyMappings ? actualResourceType.EpmSourceTree.Root : null); epmSerializer.Serialize(content, this.Provider); } if (actualResourceType.IsMediaLinkEntry) { // Write <content type="..." src="..." /> Debug.Assert(readStreamUri != null, "readStreamUri != null"); Debug.Assert(!string.IsNullOrEmpty(mediaContentType), "!string.IsNullOrEmpty(mediaContentType)"); target.Content = new UrlSyndicationContent(readStreamUri, mediaContentType); if (!content.IsEmpty) { // Since UrlSyndicationContent must have empty content, we write the <m:property /> node as SyndicationElementExtension. target.ElementExtensions.Add(content.GetPropertyContentsReader()); } } else { target.Content = content; } } #if ASTORIA_FF_CALLBACKS this.Service.InternalOnWriteItem(target, element); #endif }
/// <summary> /// Writes the feed element for the atom payload. /// </summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="elements">Collection of entries in the feed element.</param> /// <param name="expectedType">ExpectedType of the elements in the collection.</param> /// <param name="title">Title of the feed element.</param> /// <param name="getRelativeUri">Callback to get the relative uri of the feed.</param> /// <param name="getAbsoluteUri">Callback to get the absolute uri of the feed.</param> /// <param name="topLevel">True if the feed is the top level feed, otherwise false for the inner expanded feed.</param> private void WriteFeedElements( IExpandedResult expanded, QueryResultInfo elements, ResourceType expectedType, string title, Func<Uri> getRelativeUri, Func<Uri> getAbsoluteUri, bool topLevel) { Debug.Assert(elements != null, "elements != null"); Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType"); Debug.Assert(!string.IsNullOrEmpty(title), "!string.IsNullOrEmpty(title)"); ODataFeed feed = new ODataFeed(); feed.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo { NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName }); // Write the other elements for the feed this.PayloadMetadataPropertyManager.SetId(feed, () => getAbsoluteUri()); #pragma warning disable 618 if (this.contentFormat == ODataFormat.Atom) #pragma warning restore 618 { // Create the atom feed metadata and set the self link and title value. AtomFeedMetadata feedMetadata = new AtomFeedMetadata(); feed.SetAnnotation(feedMetadata); feedMetadata.Title = new AtomTextConstruct { Text = title }; feedMetadata.SelfLink = new AtomLinkMetadata { Href = getRelativeUri(), Title = title }; } // support for $count // in ATOM we write it at the beginning (we always have) // in JSON for backward compatiblity reasons we write it at the end, so we must not fill it here. if (topLevel && this.RequestDescription.CountOption == RequestQueryCountOption.CountQuery) { feed.Count = this.RequestDescription.CountValue; } var feedArgs = elements.GetDataServiceODataWriterFeedArgs(feed, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(feedArgs); object lastObject = null; IExpandedResult lastExpandedSkipToken = null; while (elements.HasMoved) { object o = elements.Current; IExpandedResult skipToken = this.GetSkipToken(expanded); if (o != null) { IExpandedResult expandedO = o as IExpandedResult; if (expandedO != null) { expanded = expandedO; o = GetExpandedElement(expanded); skipToken = this.GetSkipToken(expanded); } this.WriteEntry(expanded, o, true /*resourceInstanceInFeed*/, expectedType); } elements.MoveNext(); lastObject = o; lastExpandedSkipToken = skipToken; } // After looping through the objects in the sequence, decide if we need to write the next // page link and if yes, write it by invoking the delegate if (this.NeedNextPageLink(elements)) { this.PayloadMetadataPropertyManager.SetNextPageLink( feed, this.AbsoluteServiceUri, this.GetNextLinkUri(lastObject, lastExpandedSkipToken, getAbsoluteUri())); } this.dataServicesODataWriter.WriteEnd(feedArgs); #if ASTORIA_FF_CALLBACKS this.Service.InternalOnWriteFeed(feed); #endif }
/// <summary>Writes multiple top-level elements, possibly none.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="elements">Result elements.</param> /// <param name="hasMoved">Whether <paramref name="elements"/> was succesfully advanced to the first element.</param> protected abstract void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved);
/// <summary>Writes all the properties of the specified resource or complex object.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="customObject">Resource or complex object with properties to write out.</param> /// <param name="resourceType">resourceType containing metadata about the current custom object</param> /// <param name="absoluteUri">absolute uri for the given resource</param> /// <param name="relativeUri">relative uri for the given resource</param> /// <param name="item">Item in which to place links / expansions.</param> /// <param name="content">Content in which to place values.</param> /// <param name="currentSourceRoot">Epm source sub-tree corresponding to <paramref name="customObject"/></param> private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri absoluteUri, string relativeUri, SyndicationItem item, DictionaryContent content, EpmSourcePathSegment currentSourceRoot) { Debug.Assert(customObject != null, "customObject != null"); Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(!String.IsNullOrEmpty(relativeUri), "!String.IsNullOrEmpty(relativeUri)"); if (absoluteUri == null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) { // entity type should have an URI, complex type should not have an URI // If the static type of the object is "Object", we will mistreat an entity type as complex type and hit this situation throw new DataServiceException(500, Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(resourceType.Name)); } this.RecurseEnter(); try { List<ResourcePropertyInfo> navProperties = null; IEnumerable<ProjectionNode> projectionNodes = null; if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) { Debug.Assert(this.CurrentContainer != null, "this.CurrentContainer != null"); if (this.Provider.IsEntityTypeDisallowedForSet(this.CurrentContainer, resourceType)) { throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name)); } navProperties = new List<ResourcePropertyInfo>(resourceType.Properties.Count); projectionNodes = this.GetProjections(); } if (projectionNodes == null) { var action = resourceType.DictionarySerializerDelegate; if (action == null && this.Provider.IsV1Provider) { Module module = typeof(SyndicationSerializer).Module; Type customObjectType = customObject.GetType(); Type[] parameterTypes = new Type[] { typeof(object), typeof(DictionaryContent) }; DynamicMethod method = new DynamicMethod("content_populator", typeof(void), parameterTypes, module, false /* skipVisibility */); ILGenerator generator = method.GetILGenerator(); MethodInfo methodWritePrimitiveValue = typeof(SyndicationSerializer).GetMethod("WritePrimitiveValue", BindingFlags.Static | BindingFlags.NonPublic); // Downcast the argument. generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Castclass, customObjectType); foreach (ResourceProperty property in resourceType.Properties.Where(p => p.TypeKind == ResourceTypeKind.Primitive)) { if (SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, property.Name)) { continue; } // WritePrimitiveValue(propertyValue, property.Name, property.ResourceType, content); generator.Emit(OpCodes.Dup); generator.Emit(OpCodes.Call, resourceType.GetPropertyInfo(property).GetGetMethod()); if (property.Type.IsValueType) { generator.Emit(OpCodes.Box, property.Type); } generator.Emit(OpCodes.Ldstr, property.Name); generator.Emit(OpCodes.Ldstr, property.ResourceType.FullName); generator.Emit(OpCodes.Ldarg_1); generator.Emit(OpCodes.Call, methodWritePrimitiveValue); } generator.Emit(OpCodes.Pop); generator.Emit(OpCodes.Ret); action = (Action<object, DictionaryContent>)method.CreateDelegate(typeof(Action<object, DictionaryContent>), null); resourceType.DictionarySerializerDelegate = action; } if (action != null) { action(customObject, content); } else { foreach (ResourceProperty property in resourceType.Properties.Where(p => p.TypeKind == ResourceTypeKind.Primitive)) { object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, property, null); if (SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, property.Name)) { continue; } WritePrimitiveValue(propertyValue, property.Name, property.ResourceType.FullName, content); } } foreach (ResourceProperty property in this.Provider.GetResourceProperties(this.CurrentContainer, resourceType)) { string propertyName = property.Name; if (property.TypeKind == ResourceTypeKind.EntityType) { Debug.Assert(navProperties != null, "navProperties list must be assigned for entity types"); object propertyValue = (this.ShouldExpandSegment(property.Name)) ? GetExpandedProperty(this.Provider, expanded, customObject, property) : null; navProperties.Add(new ResourcePropertyInfo(property, propertyValue)); } else { if (property.TypeKind == ResourceTypeKind.ComplexType) { object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, property, null); bool needPop = this.PushSegmentForProperty(property); this.WriteComplexObjectValue( propertyValue, propertyName, property.ResourceType, relativeUri + "/" + property.Name, content, SyndicationSerializer.EpmGetComplexPropertySegment(currentSourceRoot, property.Name)); this.PopSegmentName(needPop); } } } if (resourceType.IsOpenType) { IEnumerable<KeyValuePair<string, object>> properties = this.Provider.GetOpenPropertyValues(customObject); foreach (KeyValuePair<string, object> property in properties) { string propertyName = property.Key; if (String.IsNullOrEmpty(propertyName)) { throw new DataServiceException(500, Strings.Syndication_InvalidOpenPropertyName(resourceType.FullName)); } Type valueType; ResourceType propertyResourceType; object value = property.Value; if (value == null || value == DBNull.Value) { valueType = typeof(string); propertyResourceType = ResourceType.PrimitiveStringResourceType; } else { valueType = value.GetType(); propertyResourceType = WebUtil.GetResourceType(this.Provider, value); } // A null ResourceType indicates a ----ed type (eg, IntPtr or DateTimeOffset). So ignore it. if (propertyResourceType == null) { throw new DataServiceException(500, Strings.Syndication_InvalidOpenPropertyType(propertyName)); } if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.Primitive) { if (value != null && SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, propertyName)) { continue; } WritePrimitiveValue(value, propertyName, propertyResourceType.FullName, content); } else { if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) { Debug.Assert(propertyResourceType.InstanceType == valueType, "propertyResourceType.Type == valueType"); this.WriteComplexObjectValue( value, propertyName, propertyResourceType, relativeUri + "/" + propertyName, content, SyndicationSerializer.EpmGetComplexPropertySegment(currentSourceRoot, propertyName)); } else { Debug.Assert( propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType -- otherwise should have been processed as primitve or complex type."); // Open navigation properties are not supported on OpenTypes throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName)); } } } } } else { foreach (ProjectionNode projectionNode in projectionNodes) { string propertyName = projectionNode.PropertyName; ResourceProperty property = resourceType.TryResolvePropertyName(propertyName); // First solve the normal entity type property - turn it into a nav. property record if (property != null && property.TypeKind == ResourceTypeKind.EntityType) { Debug.Assert(navProperties != null, "navProperties list must be assigned for entity types"); // By calling the GetResourceProperties we will use the cached list of properties // for the given type and set. But we have to search through it. // We could use the GetContainer (since that's what the GetResourceProperties does) and check // if it returns null, but result of that is only partially cached so it might be expensive // to evaluate for each item in the feed. if (this.Provider.GetResourceProperties(this.CurrentContainer, resourceType).Contains(property)) { object expandedPropertyValue = (this.ShouldExpandSegment(propertyName)) ? GetExpandedProperty(this.Provider, expanded, customObject, property) : null; navProperties.Add(new ResourcePropertyInfo(property, expandedPropertyValue)); } continue; } // Now get the property value object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, property, property == null ? propertyName : null); // Determine the type of the property ResourceType propertyResourceType; if (property != null) { propertyResourceType = property.ResourceType; } else { if (propertyValue == null || propertyValue == DBNull.Value) { propertyResourceType = ResourceType.PrimitiveStringResourceType; } else { propertyResourceType = WebUtil.GetResourceType(this.Provider, propertyValue); // A null ResourceType indicates a ----ed type (eg, IntPtr or DateTimeOffset). So ignore it. if (propertyResourceType == null) { throw new DataServiceException(500, Strings.Syndication_InvalidOpenPropertyType(propertyName)); } } } // And write out the value (depending on the type of the property) if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.Primitive) { if (propertyValue == DBNull.Value) { propertyValue = null; } if (propertyValue != null && SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, propertyName)) { continue; } WritePrimitiveValue(propertyValue, propertyName, propertyResourceType.FullName, content); } else if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) { bool needPop = false; if (property != null) { needPop = this.PushSegmentForProperty(property); } this.WriteComplexObjectValue( propertyValue, propertyName, propertyResourceType, relativeUri + "/" + propertyName, content, SyndicationSerializer.EpmGetComplexPropertySegment(currentSourceRoot, propertyName)); if (property != null) { this.PopSegmentName(needPop); } } else { Debug.Assert( propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType -- otherwise should have been processed as primitve or complex type."); // Open navigation properties are not supported on OpenTypes throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName)); } } } if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) { for (int i = 0; i < navProperties.Count; i++) { ResourcePropertyInfo propertyInfo = navProperties[i]; ResourceProperty navProperty = propertyInfo.Property; Debug.Assert( navProperty.IsOfKind(ResourcePropertyKind.ResourceReference) || navProperty.IsOfKind(ResourcePropertyKind.ResourceSetReference), "this must be nav property"); // Generate a link - see http://tools.ietf.org/html/rfc4287#section-4.2.7 string linkType = navProperty.IsOfKind(ResourcePropertyKind.ResourceReference) ? XmlConstants.AtomEntryElementName : XmlConstants.AtomFeedElementName; linkType = String.Format(CultureInfo.InvariantCulture, "{0};{1}={2}", XmlConstants.MimeApplicationAtom, XmlConstants.AtomTypeAttributeName, linkType); string segmentIdentifier = navProperty.Name; if (!this.ShouldExpandSegment(navProperty.Name)) { WriteDeferredContentElement( XmlConstants.DataWebRelatedNamespace + navProperty.Name, navProperty.Name, relativeUri + "/" + segmentIdentifier, linkType, item); } else { object propertyValue = propertyInfo.Value; IExpandedResult expandedResultPropertyValue = propertyValue as IExpandedResult; object expandedPropertyValue = expandedResultPropertyValue != null ? GetExpandedElement(expandedResultPropertyValue) : propertyValue; string propertyRelativeUri = relativeUri + "/" + segmentIdentifier; Uri propertyAbsoluteUri = RequestUriProcessor.AppendUnescapedSegment(absoluteUri, segmentIdentifier); SyndicationLink link = new SyndicationLink(); link.RelationshipType = XmlConstants.DataWebRelatedNamespace + navProperty.Name; link.Title = navProperty.Name; link.Uri = new Uri(propertyRelativeUri, UriKind.RelativeOrAbsolute); link.MediaType = linkType; item.Links.Add(link); bool needPop = this.PushSegmentForProperty(navProperty); // if this.CurrentContainer is null, the target set of the navigation property is hidden. if (this.CurrentContainer != null) { if (navProperty.IsOfKind(ResourcePropertyKind.ResourceSetReference)) { IEnumerable enumerable; bool collection = WebUtil.IsElementIEnumerable(expandedPropertyValue, out enumerable); Debug.Assert(collection, "metadata loading must have ensured that navigation set properties must implement IEnumerable"); SyndicationFeed feed = new SyndicationFeed(); InlineAtomFeed inlineFeedExtension = new InlineAtomFeed(feed, this.factory); link.ElementExtensions.Add(inlineFeedExtension); IEnumerator enumerator = enumerable.GetEnumerator(); try { bool hasMoved = enumerator.MoveNext(); this.WriteFeedElements( propertyValue as IExpandedResult, enumerator, navProperty.ResourceType, navProperty.Name, propertyAbsoluteUri, propertyRelativeUri, hasMoved, feed, true); } catch { WebUtil.Dispose(enumerator); throw; } } else { SyndicationItem inlineItem = new SyndicationItem(); this.WriteEntryElement(propertyValue as IExpandedResult, expandedPropertyValue, navProperty.ResourceType, propertyAbsoluteUri, propertyRelativeUri, inlineItem); InlineAtomItem inlineItemExtension = new InlineAtomItem(inlineItem, this.factory); link.ElementExtensions.Add(inlineItemExtension); } } this.PopSegmentName(needPop); } } } } finally { // The matching call to RecurseLeave is in a try/finally block not because it's necessary in the // presence of an exception (progress will halt anyway), but because it's easier to maintain in the // code in the presence of multiple exit points (returns). this.RecurseLeave(); } }
/// <summary>Writes a single top-level element.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="element">Element to write, possibly null.</param> protected override void WriteTopLevelElement(IExpandedResult expanded, object element) { Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult"); Debug.Assert(element != null, "element != null"); this.resultItem = new SyndicationItem(); this.resultItem.BaseUri = this.AbsoluteServiceUri; IncludeCommonNamespaces(this.resultItem.AttributeExtensions); if (this.RequestDescription.TargetSource == RequestTargetSource.EntitySet || this.RequestDescription.TargetSource == RequestTargetSource.ServiceOperation) { bool needPop = this.PushSegmentForRoot(); this.WriteEntryElement( expanded, element, this.RequestDescription.TargetResourceType, this.RequestDescription.ResultUri, this.RequestDescription.ContainerName, this.resultItem); this.PopSegmentName(needPop); } else { Debug.Assert( this.RequestDescription.TargetSource == RequestTargetSource.Property, "TargetSource(" + this.RequestDescription.TargetSource + ") == Property -- otherwise control shouldn't be here."); ResourceType resourcePropertyType; if (this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty) { resourcePropertyType = (element == null) ? ResourceType.PrimitiveStringResourceType : WebUtil.GetResourceType(this.Provider, element); if (resourcePropertyType == null) { Type propertyType = element == null ? typeof(string) : element.GetType(); throw new InvalidOperationException(Strings.Serializer_UnsupportedTopLevelType(propertyType)); } } else { Debug.Assert(this.RequestDescription.Property != null, "this.RequestDescription.Property - otherwise Property source set with no Property specified."); ResourceProperty property = this.RequestDescription.Property; resourcePropertyType = property.ResourceType; } Debug.Assert( resourcePropertyType.ResourceTypeKind == ResourceTypeKind.EntityType, "Open ResourceTypeKind == EnityType -- temporarily, because ATOM is the only implemented syndication serializer and doesn't support it."); bool needPop = this.PushSegmentForRoot(); this.WriteEntryElement( expanded, // expanded element, // element resourcePropertyType, // expectedType this.RequestDescription.ResultUri, // absoluteUri this.RequestDescription.ContainerName, // relativeUri this.resultItem); // target this.PopSegmentName(needPop); } // Since the element is not equal to null, the factory should never return null SyndicationItemFormatter formatter = this.factory.CreateSyndicationItemFormatter(this.resultItem); formatter.WriteTo(this.writer); }
/// <summary>Provides an enumeration of deferred feed items.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="elements">Elements to enumerate.</param> /// <param name="expectedType">Expected type of elements.</param> /// <param name="hasMoved">Whether the enumerator moved to the first element.</param> /// <param name="activeSegmentNames">The segment names active at this point in serialization.</param> /// <param name="nextPageLinkWriter">Delegate that writes the next page link if necessity arises</param> /// <param name="disposeElements">If set to true the function should dispose the elements enumerator (always).</param> /// <returns>An object that can enumerate syndication items.</returns> private IEnumerable<SyndicationItem> DeferredFeedItems( IExpandedResult expanded, IEnumerator elements, ResourceType expectedType, bool hasMoved, object activeSegmentNames, Action<object, IExpandedResult> nextPageLinkWriter, bool disposeElements) { try { object savedSegmentNames = this.SaveSegmentNames(); this.RestoreSegmentNames(activeSegmentNames); object lastObject = null; IExpandedResult lastExpandedSkipToken = null; while (hasMoved) { object o = elements.Current; IExpandedResult skipToken = this.GetSkipToken(expanded); if (o != null) { SyndicationItem target = new SyndicationItem(); IExpandedResult expandedO = o as IExpandedResult; if (expandedO != null) { expanded = expandedO; o = GetExpandedElement(expanded); skipToken = this.GetSkipToken(expanded); } this.WriteEntryElement(expanded, o, expectedType, null, null, target); yield return target; } hasMoved = elements.MoveNext(); lastObject = o; lastExpandedSkipToken = skipToken; } // After looping through the objects in the sequence, decide if we need to write the next // page link and if yes, write it by invoking the delegate if (this.NeedNextPageLink(elements)) { nextPageLinkWriter(lastObject, lastExpandedSkipToken); } this.RestoreSegmentNames(savedSegmentNames); } finally { if (disposeElements) { WebUtil.Dispose(elements); } } }
/// <summary> /// Writes the next page link to the current xml writer corresponding to the feed /// </summary> /// <param name="lastElement">Object that will contain the keys for skip token</param> /// <param name="expandedResult">The <see cref="IExpandedResult"/> of the $skiptoken property of the object being written</param> /// <param name="absoluteUri">Absolute URI for the result</param> private void WriteNextPageLink(object lastElement, IExpandedResult expandedResult, Uri absoluteUri) { this.writer.WriteStartElement("link", XmlConstants.AtomNamespace); this.writer.WriteAttributeString("rel", "next"); this.writer.WriteAttributeString("href", this.GetNextLinkUri(lastElement, expandedResult, absoluteUri)); this.writer.WriteEndElement(); }
/// <summary> /// Writes the feed element for the atom payload /// </summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="elements">collection of entries in the feed element</param> /// <param name="expectedType">expectedType of the elements in the collection</param> /// <param name="title">title of the feed element</param> /// <param name="absoluteUri">absolute uri representing the feed element</param> /// <param name="relativeUri">relative uri representing the feed element</param> /// <param name="hasMoved">whether the enumerator has successfully moved to the first element</param> /// <param name='feed'>Feed to write to.</param> /// <param name="disposeElementsOnSuccess">If set to true the function should dispose the elements enumerator when it's done /// with it. Not in the case this method fails though.</param> private void WriteFeedElements( IExpandedResult expanded, IEnumerator elements, ResourceType expectedType, string title, Uri absoluteUri, string relativeUri, bool hasMoved, SyndicationFeed feed, bool disposeElementsOnSuccess) { Debug.Assert(feed != null, "feed != null"); // Write the other elements for the feed feed.Id = absoluteUri.AbsoluteUri; feed.Title = new TextSyndicationContent(title); var uri = new Uri(relativeUri, UriKind.Relative); var link = new SyndicationLink(uri, XmlConstants.AtomSelfRelationAttributeValue, title, null, 0L); feed.Links.Add(link); if (!hasMoved) { // ATOM specification: if a feed contains no entries, then the feed should have at least one Author tag feed.Authors.Add(EmptyPerson); } // Instead of looping, create an item that will defer the production of SyndicationItem instances. // PERF: consider optimizing out empty collections when hasMoved is false. feed.Items = this.DeferredFeedItems( expanded, elements, expectedType, hasMoved, this.SaveSegmentNames(), (o, e) => this.WriteNextPageLink(o, e, absoluteUri), disposeElementsOnSuccess); #if ASTORIA_FF_CALLBACKS this.Service.InternalOnWriteFeed(feed); #endif }
private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri parentUri, bool objectIsResource) { Debug.Assert(customObject != null, "customObject != null"); Debug.Assert(resourceType != null, "customObjectType != null"); Debug.Assert( ((customObject is IProjectedResult) && (resourceType.FullName == ((IProjectedResult)customObject).ResourceTypeName)) || (resourceType.InstanceType.IsAssignableFrom(customObject.GetType())), "The type of the object doesn't match the resource type specified."); Debug.Assert(parentUri != null, "parentUri != null"); Debug.Assert(resourceType.ResourceTypeKind != ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive"); // We should throw while if there are navigation properties in the derived entity type if (this.CurrentContainer != null && this.Provider.IsEntityTypeDisallowedForSet(this.CurrentContainer, resourceType)) { throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name)); } IEnumerable<ProjectionNode> projectionNodes = null; if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) { projectionNodes = this.GetProjections(); } if (projectionNodes == null) { foreach (ResourceProperty property in this.Provider.GetResourceProperties(this.CurrentContainer, resourceType)) { Debug.Assert( objectIsResource || !property.IsOfKind(ResourcePropertyKind.Key), "objectIsResource || property.Kind != ResourcePropertyKind.KeyPrimitive - complex types shouldn't have key properties"); this.WriteObjectDeclaredProperty(expanded, customObject, property, parentUri); } if (resourceType.IsOpenType) { foreach (var pair in this.Provider.GetOpenPropertyValues(customObject)) { this.WriteObjectOpenProperty(pair.Key, pair.Value, parentUri); } } } else { foreach (ProjectionNode projectionNode in projectionNodes) { string propertyName = projectionNode.PropertyName; ResourceProperty property = resourceType.TryResolvePropertyName(propertyName); if (property != null) { if (property.TypeKind != ResourceTypeKind.EntityType || this.Provider.GetResourceProperties(this.CurrentContainer, resourceType).Contains(property)) { this.WriteObjectDeclaredProperty(expanded, customObject, property, parentUri); } } else { object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, null, propertyName); this.WriteObjectOpenProperty(propertyName, propertyValue, parentUri); } } } }
private void WriteNavigationProperties(IExpandedResult expanded, EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, IEnumerable<ProjectionNode> projectionNodesForCurrentResourceType) { Debug.Assert(entityToSerialize != null, "entityToSerialize != null"); Debug.Assert( projectionNodesForCurrentResourceType == null || projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)), "The projection nodes must be filtered to the current resource type only."); IEnumerable<ResourceProperty> navProperties = projectionNodesForCurrentResourceType == null ? this.Provider.GetResourceSerializableProperties(this.CurrentContainer, entityToSerialize.ResourceType).Where(p => p.TypeKind == ResourceTypeKind.EntityType) : projectionNodesForCurrentResourceType.Where(p => p.Property != null && p.Property.TypeKind == ResourceTypeKind.EntityType).Select(p1 => p1.Property); foreach (ResourceProperty property in navProperties) { ResourcePropertyInfo navProperty = this.GetNavigationPropertyInfo(expanded, entityToSerialize.Entity, entityToSerialize.ResourceType, property); ODataNavigationLink navLink = this.GetNavigationLink(entityToSerialize, navProperty.Property); // WCF Data Services show performance degradation with JsonLight when entities have > 25 Navgations on writing entries // DEVNOTE: for performance reasons, if the link has no content due to the metadata level, and is not expanded // then don't tell ODataLib about it at all. if (navLink.Url == null && navLink.AssociationLinkUrl == null && !navProperty.Expand) { continue; } var linkArgs = new DataServiceODataWriterNavigationLinkArgs(navLink, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(linkArgs); if (navProperty.Expand) { object navPropertyValue = navProperty.Value; IExpandedResult navPropertyExpandedResult = navPropertyValue as IExpandedResult; object expandedPropertyValue = navPropertyExpandedResult != null ? GetExpandedElement(navPropertyExpandedResult) : navPropertyValue; bool needPop = this.PushSegmentForProperty(navProperty.Property, entityToSerialize.ResourceType, navProperty.ExpandedNode); // if this.CurrentContainer is null, the target set of the navigation property is hidden. if (this.CurrentContainer != null) { if (navProperty.Property.Kind == ResourcePropertyKind.ResourceSetReference) { IEnumerable enumerable; bool collection = WebUtil.IsElementIEnumerable(expandedPropertyValue, out enumerable); Debug.Assert(collection, "metadata loading must have ensured that navigation set properties must implement IEnumerable"); QueryResultInfo queryResults = new QueryResultInfo(enumerable); try { queryResults.MoveNext(); Func<Uri> getNavPropertyRelativeUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navLink.Name); Func<Uri> getNavPropertyAbsoluteUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLink, navLink.Name); this.WriteFeedElements(navPropertyExpandedResult, queryResults, navProperty.Property.ResourceType, navLink.Name, getNavPropertyRelativeUri, getNavPropertyAbsoluteUri, false); } finally { // After the navigation property is completely serialized, dispose the property value. WebUtil.Dispose(queryResults); } } else if (WebUtil.IsNullValue(expandedPropertyValue)) { // Write a null reference navigation property var entryArgs = new DataServiceODataWriterEntryArgs(null, null, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(entryArgs); this.dataServicesODataWriter.WriteEnd(entryArgs); } else { this.WriteEntry(navPropertyExpandedResult, expandedPropertyValue, resourceInstanceInFeed, navProperty.Property.ResourceType); } } this.PopSegmentName(needPop); } this.dataServicesODataWriter.WriteEnd(linkArgs); // end of navigation property } }
private void WriteNavigationProperties(IExpandedResult expanded, EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, IEnumerable <ProjectionNode> projectionNodesForCurrentResourceType) { Debug.Assert(entityToSerialize != null, "entityToSerialize != null"); Debug.Assert( projectionNodesForCurrentResourceType == null || projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)), "The projection nodes must be filtered to the current resource type only."); IEnumerable <ResourceProperty> navProperties = projectionNodesForCurrentResourceType == null ? this.Provider.GetResourceSerializableProperties(this.CurrentContainer, entityToSerialize.ResourceType).Where(p => p.TypeKind == ResourceTypeKind.EntityType) : projectionNodesForCurrentResourceType.Where(p => p.Property != null && p.Property.TypeKind == ResourceTypeKind.EntityType).Select(p1 => p1.Property); foreach (ResourceProperty property in navProperties) { ResourcePropertyInfo navProperty = this.GetNavigationPropertyInfo(expanded, entityToSerialize.Entity, entityToSerialize.ResourceType, property); ODataNavigationLink navLink = this.GetNavigationLink(entityToSerialize, navProperty.Property); // WCF Data Services show performance degradation with JsonLight when entities have > 25 Navgations on writing entries // DEVNOTE: for performance reasons, if the link has no content due to the metadata level, and is not expanded // then don't tell ODataLib about it at all. if (navLink.Url == null && navLink.AssociationLinkUrl == null && !navProperty.Expand) { continue; } var linkArgs = new DataServiceODataWriterNavigationLinkArgs(navLink, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(linkArgs); if (navProperty.Expand) { object navPropertyValue = navProperty.Value; IExpandedResult navPropertyExpandedResult = navPropertyValue as IExpandedResult; object expandedPropertyValue = navPropertyExpandedResult != null? GetExpandedElement(navPropertyExpandedResult) : navPropertyValue; bool needPop = this.PushSegmentForProperty(navProperty.Property, entityToSerialize.ResourceType, navProperty.ExpandedNode); // if this.CurrentContainer is null, the target set of the navigation property is hidden. if (this.CurrentContainer != null) { if (navProperty.Property.Kind == ResourcePropertyKind.ResourceSetReference) { IEnumerable enumerable; bool collection = WebUtil.IsElementIEnumerable(expandedPropertyValue, out enumerable); Debug.Assert(collection, "metadata loading must have ensured that navigation set properties must implement IEnumerable"); QueryResultInfo queryResults = new QueryResultInfo(enumerable); try { queryResults.MoveNext(); Func <Uri> getNavPropertyRelativeUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navLink.Name); Func <Uri> getNavPropertyAbsoluteUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLink, navLink.Name); this.WriteFeedElements(navPropertyExpandedResult, queryResults, navProperty.Property.ResourceType, navLink.Name, getNavPropertyRelativeUri, getNavPropertyAbsoluteUri, false); } finally { // After the navigation property is completely serialized, dispose the property value. WebUtil.Dispose(queryResults); } } else if (WebUtil.IsNullValue(expandedPropertyValue)) { // Write a null reference navigation property var entryArgs = new DataServiceODataWriterEntryArgs(null, null, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(entryArgs); this.dataServicesODataWriter.WriteEnd(entryArgs); } else { this.WriteEntry(navPropertyExpandedResult, expandedPropertyValue, resourceInstanceInFeed, navProperty.Property.ResourceType); } } this.PopSegmentName(needPop); } this.dataServicesODataWriter.WriteEnd(linkArgs); // end of navigation property } }
/// <summary>Write the entry element.</summary> /// <param name="expanded">Expanded result provider for the specified <paramref name="element"/>.</param> /// <param name="element">Element representing the entry element.</param> /// <param name="resourceInstanceInFeed">true if the resource instance being serialized is inside a feed; false otherwise.</param> /// <param name="expectedType">Expected type of the entry element.</param> private void WriteEntry(IExpandedResult expanded, object element, bool resourceInstanceInFeed, ResourceType expectedType) { Debug.Assert(element != null, "element != null"); Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType"); this.IncrementSegmentResultCount(); ODataEntry entry = new ODataEntry(); if (!resourceInstanceInFeed) { entry.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo { NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName }); } string title = expectedType.Name; #pragma warning disable 618 if (this.contentFormat == ODataFormat.Atom) #pragma warning restore 618 { AtomEntryMetadata entryAtom = new AtomEntryMetadata(); entryAtom.EditLink = new AtomLinkMetadata { Title = title }; entry.SetAnnotation(entryAtom); } ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element); if (actualResourceType.ResourceTypeKind != ResourceTypeKind.EntityType) { // making sure that the actual resource type is an entity type throw new DataServiceException(500, Microsoft.OData.Service.Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(actualResourceType.FullName)); } EntityToSerialize entityToSerialize = this.WrapEntity(element, actualResourceType); // populate the media resource, if the entity is a MLE. entry.MediaResource = this.GetMediaResource(entityToSerialize, title); // Write the type name this.PayloadMetadataPropertyManager.SetTypeName(entry, this.CurrentContainer.ResourceType.FullName, actualResourceType.FullName); // Write Id element this.PayloadMetadataPropertyManager.SetId(entry, () => entityToSerialize.SerializedKey.Identity); // Write "edit" link this.PayloadMetadataPropertyManager.SetEditLink(entry, () => entityToSerialize.SerializedKey.RelativeEditLink); // Write the etag property, if the type has etag properties this.PayloadMetadataPropertyManager.SetETag(entry, () => this.GetETagValue(element, actualResourceType)); IEnumerable<ProjectionNode> projectionNodes = this.GetProjections(); if (projectionNodes != null) { // Filter the projection nodes for the actual type of the entity // The projection node might refer to the property in a derived type. If the TargetResourceType of // the projection node is not a super type, then we do not want to serialize this property. projectionNodes = projectionNodes.Where(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(actualResourceType)); // Because we are going to enumerate through these multiple times, create a list. projectionNodes = projectionNodes.ToList(); // And add the annotation to tell ODataLib which properties to write into content (the projections) entry.SetAnnotation(new ProjectedPropertiesAnnotation(projectionNodes.Select(p => p.PropertyName))); } // Populate the advertised actions IEnumerable<ODataAction> actions; if (this.TryGetAdvertisedActions(entityToSerialize, resourceInstanceInFeed, out actions)) { foreach (ODataAction action in actions) { entry.AddAction(action); } } // Populate all the normal properties entry.Properties = this.GetEntityProperties(entityToSerialize, projectionNodes); // And start the entry var args = new DataServiceODataWriterEntryArgs(entry, element, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(args); // Now write all the navigation properties this.WriteNavigationProperties(expanded, entityToSerialize, resourceInstanceInFeed, projectionNodes); // And write the end of the entry this.dataServicesODataWriter.WriteEnd(args); #if ASTORIA_FF_CALLBACKS this.Service.InternalOnWriteItem(target, element); #endif }
/// <summary>Gets the expandable value for the specified object.</summary> /// <param name="provider">underlying data source instance.</param> /// <param name="expanded">Expanded properties for the result, possibly null.</param> /// <param name="customObject">Object with value to retrieve.</param> /// <param name="property">Property for which value will be retrieved.</param> /// <returns>The property value.</returns> protected static object GetExpandedProperty(DataServiceProviderWrapper provider, IExpandedResult expanded, object customObject, ResourceProperty property) { Debug.Assert(property != null, "property != null"); if (expanded == null) { return WebUtil.GetPropertyValue(provider, customObject, null, property, null); } else { // We may end up projecting null as a value of ResourceSetReference property. This can in theory break // the serializers as they expect a non-null (possibly empty) IEnumerable instead. But note that // if we project null into the expanded property, we also project null into the ExpandedElement property // and thus the serializers should recognize this value as null and don't try to expand its properties. Debug.Assert( expanded.ExpandedElement != null, "We should not be accessing expanded properties on null resource."); return expanded.GetExpandedPropertyValue(property.Name); } }
private void WriteElementWithName(IExpandedResult expanded, object element, string elementName, Uri elementUri, bool topLevel) { Debug.Assert(elementName == null || elementName.Length > 0, "elementName == null || elementName.Length > 0"); Debug.Assert(elementUri != null, "elementUri != null"); this.RecurseEnter(); try { if (element == null) { if (topLevel) { this.writer.StartObjectScope(); this.writer.WriteName(elementName); this.WriteNullValue(); this.writer.EndScope(); } else { this.WriteNullValue(); } } else { IEnumerable enumerableElement; if (WebUtil.IsElementIEnumerable(element, out enumerableElement)) { if (this.JsonFormatVersion >= 2) { // JSON V2 Nested Collections: // { // "results": [] // "__next" : uri // } this.writer.StartObjectScope(); this.writer.WriteDataArrayName(); } this.writer.StartArrayScope(); IEnumerator elements = enumerableElement.GetEnumerator(); try { IExpandedResult expandedEnumerator = elements as IExpandedResult; object lastObject = null; IExpandedResult lastExpandedSkipToken = null; while (elements.MoveNext()) { object elementInCollection = elements.Current; IExpandedResult skipToken = this.GetSkipToken(expandedEnumerator); if (elementInCollection != null) { IExpandedResult expandedElementInCollection = elementInCollection as IExpandedResult; if (expandedElementInCollection != null) { expandedEnumerator = expandedElementInCollection; elementInCollection = GetExpandedElement(expandedEnumerator); skipToken = this.GetSkipToken(expandedEnumerator); } this.WriteElementWithName(expandedEnumerator, elementInCollection, null, elementUri, false /*topLevel*/); } lastObject = elementInCollection; lastExpandedSkipToken = skipToken; } this.writer.EndScope(); // array scope if (this.JsonFormatVersion >= 2) { // After looping through the objects in the sequence, decide if we need to write the next // page link and if yes, write it by invoking the delegate if (this.NeedNextPageLink(elements)) { this.WriteNextPageLink(lastObject, lastExpandedSkipToken, elementUri); } this.writer.EndScope(); // expanded object scope } } finally { WebUtil.Dispose(elements); } return; } ResourceType resourceType = WebUtil.GetResourceType(this.Provider, element); if (resourceType == null) { // Skip this element. return; } switch (resourceType.ResourceTypeKind) { case ResourceTypeKind.ComplexType: if (topLevel) { this.writer.StartObjectScope(); this.writer.WriteName(elementName); } // Non-value complex types may form a cycle. // PERF: we can keep a single element around and save the HashSet initialization // until we find a second complex type - this saves the allocation on trees // with shallow (single-level) complex types. if (this.AddToComplexTypeCollection(element)) { this.WriteComplexTypeProperties(element, resourceType, elementUri); this.RemoveFromComplexTypeCollection(element); } else { throw new InvalidOperationException(Strings.Serializer_LoopsNotAllowedInComplexTypes(elementName)); } if (topLevel) { this.writer.EndScope(); } break; case ResourceTypeKind.EntityType: this.IncrementSegmentResultCount(); this.writer.StartObjectScope(); Uri entityUri = Serializer.GetUri(element, this.Provider, this.CurrentContainer, this.AbsoluteServiceUri); this.WriteMetadataObject(element, resourceType, entityUri); this.WriteResourceProperties(expanded, element, resourceType, entityUri); this.writer.EndScope(); break; default: Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive"); if (topLevel) { this.writer.StartObjectScope(); this.writer.WriteName(elementName); this.WritePrimitiveValue(element); this.writer.EndScope(); } else { this.WritePrimitiveValue(element); } break; } } } finally { // The matching call to RecurseLeave is in a try/finally block not because it's necessary in the // presence of an exception (progress will halt anyway), but because it's easier to maintain in the // code in the presence of multiple exit points (returns). this.RecurseLeave(); } }
/// <summary>Constructor.</summary> /// <param name="skipTokenExpandedResult">IExpandedResult to lookup for skip token values.</param> /// <param name="skipTokenExpressionCount">Number of values in skip token.</param> public SkipTokenBuilderFromExpandedResult(IExpandedResult skipTokenExpandedResult, int skipTokenExpressionCount) : base() { this.skipTokenExpandedResult = skipTokenExpandedResult; this.skipTokenExpressionCount = skipTokenExpressionCount; }
/// <summary>Writes a single top-level element.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="element">Element to write, possibly null.</param> protected abstract void WriteTopLevelElement(IExpandedResult expanded, object element);
/// <summary>Writes multiple top-level elements, possibly none.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="elements">Enumerator for elements to write.</param> /// <param name="hasMoved">Whether <paramref name="elements"/> was succesfully advanced to the first element.</param> protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved) { Debug.Assert(elements != null, "elements != null"); Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult"); string title; if (this.RequestDescription.TargetKind != RequestTargetKind.OpenProperty && this.RequestDescription.TargetSource == RequestTargetSource.Property) { title = this.RequestDescription.Property.Name; } else { title = this.RequestDescription.ContainerName; } this.resultFeed = new SyndicationFeed(); IncludeCommonNamespaces(this.resultFeed.AttributeExtensions); this.resultFeed.BaseUri = RequestUriProcessor.AppendEscapedSegment(this.AbsoluteServiceUri, ""); string relativeUri = this.RequestDescription.LastSegmentInfo.Identifier; // support for $count if (this.RequestDescription.CountOption == RequestQueryCountOption.Inline) { this.WriteRowCount(); } bool needPop = this.PushSegmentForRoot(); this.WriteFeedElements( expanded, elements, this.RequestDescription.TargetResourceType, title, // title this.RequestDescription.ResultUri, // absoluteUri relativeUri, // relativeUri hasMoved, // hasMoved this.resultFeed, // feed false); this.PopSegmentName(needPop); SyndicationFeedFormatter formatter = this.factory.CreateSyndicationFeedFormatter(this.resultFeed); formatter.WriteTo(this.writer); }
/// <summary>Writes multiple top-level elements, possibly none.</summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="elements">Enumerator for elements to write.</param> /// <param name="hasMoved">Whether <paramref name="elements"/> was succesfully advanced to the first element.</param> protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved) { Debug.Assert(elements != null, "elements != null"); Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult"); bool needPop = this.PushSegmentForRoot(); Uri parentUri = this.RequestDescription.ResultUri; this.writer.StartObjectScope(); this.writer.WriteDataWrapper(); if (this.JsonFormatVersion >= 2) { // Json Format V2: // { "d" : // { // "__results": [], // "__count": 0, // "__next" : uri // } // } this.writer.StartObjectScope(); this.writer.WriteDataArrayName(); } // Json Format V1: // { "d" : [] } this.writer.StartArrayScope(); object lastObject = null; IExpandedResult lastExpandedSkipToken = null; while (hasMoved) { object o = elements.Current; IExpandedResult skipToken = this.GetSkipToken(expanded); if (o != null) { IExpandedResult expandedObject = o as IExpandedResult; if (expandedObject != null) { expanded = expandedObject; o = GetExpandedElement(expanded); skipToken = this.GetSkipToken(expanded); } this.WriteElementWithName(expanded, o, null, parentUri, false /*topLevel*/); } hasMoved = elements.MoveNext(); lastObject = o; lastExpandedSkipToken = skipToken; } this.writer.EndScope(); // end data array if (this.JsonFormatVersion >= 2) { // $count=inline support if (this.RequestDescription.CountOption == RequestQueryCountOption.Inline) { this.WriteRowCount(); } // After looping through the objects in the sequence, decide if we need to write the next // page link and if yes, write it by invoking the delegate if (this.NeedNextPageLink(elements)) { this.WriteNextPageLink(lastObject, lastExpandedSkipToken, parentUri); } this.writer.EndScope(); // end object scope } this.writer.EndScope(); // end "d" wrapper this.PopSegmentName(needPop); }
private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri parentUri, bool objectIsResource) { Debug.Assert(customObject != null, "customObject != null"); Debug.Assert(resourceType != null, "customObjectType != null"); Debug.Assert( ((customObject is IProjectedResult) && (resourceType.FullName == ((IProjectedResult)customObject).ResourceTypeName)) || (resourceType.InstanceType.IsAssignableFrom(customObject.GetType())), "The type of the object doesn't match the resource type specified."); Debug.Assert(parentUri != null, "parentUri != null"); Debug.Assert(resourceType.ResourceTypeKind != ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive"); // We should throw while if there are navigation properties in the derived entity type if (this.CurrentContainer != null && this.Provider.IsEntityTypeDisallowedForSet(this.CurrentContainer, resourceType)) { throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name)); } IEnumerable <ProjectionNode> projectionNodes = null; if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) { projectionNodes = this.GetProjections(); } if (projectionNodes == null) { foreach (ResourceProperty property in this.Provider.GetResourceProperties(this.CurrentContainer, resourceType)) { Debug.Assert( objectIsResource || !property.IsOfKind(ResourcePropertyKind.Key), "objectIsResource || property.Kind != ResourcePropertyKind.KeyPrimitive - complex types shouldn't have key properties"); this.WriteObjectDeclaredProperty(expanded, customObject, property, parentUri); } if (resourceType.IsOpenType) { foreach (var pair in this.Provider.GetOpenPropertyValues(customObject)) { this.WriteObjectOpenProperty(pair.Key, pair.Value, parentUri); } } } else { foreach (ProjectionNode projectionNode in projectionNodes) { string propertyName = projectionNode.PropertyName; ResourceProperty property = resourceType.TryResolvePropertyName(propertyName); if (property != null) { if (property.TypeKind != ResourceTypeKind.EntityType || this.Provider.GetResourceProperties(this.CurrentContainer, resourceType).Contains(property)) { this.WriteObjectDeclaredProperty(expanded, customObject, property, parentUri); } } else { object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, null, propertyName); this.WriteObjectOpenProperty(propertyName, propertyValue, parentUri); } } } }
/// <summary> /// Writes the next page link to the current xml writer corresponding to the feed /// </summary> /// <param name="lastElement">Object that will contain the keys for skip token</param> /// <param name="skipTokenExpandedResult">The <see cref="IExpandedResult"/> of the $skiptoken property of the object being written</param> /// <param name="absoluteUri">Absolute URI for the result</param> private void WriteNextPageLink(object lastElement, IExpandedResult skipTokenExpandedResult, Uri absoluteUri) { this.writer.WriteName(XmlConstants.JsonNextString); this.writer.WriteValue(this.GetNextLinkUri(lastElement, skipTokenExpandedResult, absoluteUri)); }
/// <summary>Gets the expanded element for the specified expanded result.</summary> /// <param name="expanded">The expanded result to process.</param> /// <returns>The expanded element.</returns> protected static object GetExpandedElement(IExpandedResult expanded) { Debug.Assert(expanded != null, "expanded != null"); return expanded.ExpandedElement; }
/// <summary> /// Gets the skip token object contained in the expanded result for standard paging. /// </summary> /// <param name="expanded">Current expanded result.</param> /// <returns>Skip token object if any.</returns> protected IExpandedResult GetSkipToken(IExpandedResult expanded) { if (expanded != null && !this.IsCustomPaged && !this.RequestDescription.IsRequestForEnumServiceOperation) { return expanded.GetExpandedPropertyValue(XmlConstants.HttpQueryStringSkipToken) as IExpandedResult; } return null; }