public void ReadLinkShouldNotBeOmittedWhenNotIdenticalToEditLink() { DateTimeOffset updatedTime = DateTimeOffset.UtcNow; var entry = new ODataEntry { Id = new Uri("http://test.org/EntitySet('1')"), EditLink = new Uri("http://test.org/EntitySet('1')/edit"), ReadLink = new Uri("http://test.org/EntitySet('1')/read") }; entry.SetAnnotation(new AtomEntryMetadata() { Updated = updatedTime }); string actual = this.WriteAtomEntry(entry); string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\" m:context=\"http://temp.org/$metadata#EntitySet/$entity\">" + "<id>http://test.org/EntitySet('1')</id>" + "<link rel=\"edit\" href=\"http://test.org/EntitySet('1')/edit\" />" + "<link rel=\"self\" href=\"http://test.org/EntitySet('1')/read\" />" + "<title />" + "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" + "<author>" + "<name />" + "</author>" + "<content type=\"application/xml\" />" + "</entry>"; Assert.AreEqual(expected, actual); }
/// <summary> /// Creates a new ODataEntry from the specified entity set, instance, and type. /// </summary> /// <param name="entitySet">Entity set for the new entry.</param> /// <param name="value">Entity instance for the new entry.</param> /// <param name="entityType">Entity type for the new entry.</param> /// <returns>New ODataEntry with the specified entity set and type, property values from the specified instance.</returns> internal static ODataEntry CreateODataEntry(IEdmEntitySet entitySet, IEdmStructuredValue value, IEdmEntityType entityType) { var entry = new ODataEntry(); entry.SetAnnotation(new ODataTypeAnnotation(entitySet, entityType)); entry.Properties = value.PropertyValues.Select(p => { object propertyValue; if (p.Value.ValueKind == EdmValueKind.Null) { propertyValue = null; } else if (p.Value is IEdmPrimitiveValue) { propertyValue = ((IEdmPrimitiveValue)p.Value).ToClrValue(); } else { Assert.Fail("Test only currently supports creating ODataEntry from IEdmPrimitiveValue instances."); return null; } return new ODataProperty() { Name = p.Name, Value = propertyValue }; }); return entry; }
private static ODataEntry CreateEntryWithKeyAsSegmentConvention(bool addAnnotation, bool? useKeyAsSegment) { var model = new EdmModel(); var container = new EdmEntityContainer("Fake", "Container"); model.AddElement(container); if (addAnnotation) { model.AddVocabularyAnnotation(new EdmAnnotation(container, UrlConventionsConstants.ConventionTerm, UrlConventionsConstants.KeyAsSegmentAnnotationValue)); } EdmEntityType entityType = new EdmEntityType("Fake", "FakeType"); entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32)); model.AddElement(entityType); var entitySet = new EdmEntitySet(container, "FakeSet", entityType); container.AddElement(entitySet); var metadataContext = new ODataMetadataContext( true, ODataReaderBehavior.DefaultBehavior.OperationsBoundToEntityTypeMustBeContainerQualified, new EdmTypeReaderResolver(model, ODataReaderBehavior.DefaultBehavior), model, new Uri("http://temp.org/$metadata"), null /*requestUri*/); var thing = new ODataEntry {Properties = new[] {new ODataProperty {Name = "Id", Value = 1}}}; thing.SetAnnotation(new ODataTypeAnnotation(entitySet, entityType)); thing.MetadataBuilder = metadataContext.GetEntityMetadataBuilderForReader(new TestJsonLightReaderEntryState { Entry = thing, SelectedProperties = new SelectedPropertiesNode("*")}, useKeyAsSegment); return thing; }
internal void SetTypeName(ODataEntry entry, string entitySetBaseTypeName, string entryTypeName) { Debug.Assert(entry != null, "entry != null"); // We should always write this since for derived types, ODL needs to know the typename. entry.TypeName = entryTypeName; if (this.interpreter.ShouldIncludeEntryTypeName(entitySetBaseTypeName, entryTypeName)) { entry.SetAnnotation(new SerializationTypeNameAnnotation() { TypeName = entry.TypeName }); } else { // When we should not write the typename, setting the serialization type name to null // so that ODL does not write the type on the wire. entry.SetAnnotation(new SerializationTypeNameAnnotation() { TypeName = null }); } }
internal static IEdmValue CreateStructuredEdmValue(ODataEntry entry, IEdmEntitySet entitySet, IEdmEntityTypeReference entityType) { if (entitySet != null) { object typeAnnotation = ReflectionUtils.CreateInstance( odataTypeAnnotationType, new Type[] { typeof(IEdmEntitySet), typeof(IEdmEntityTypeReference) }, entitySet, entityType); entry.SetAnnotation(typeAnnotation); } return (IEdmValue)ReflectionUtils.CreateInstance( odataEdmStructuredValueType, new Type[] { typeof(ODataEntry) }, entry); }
/// <summary> /// If an entity type name is found in the payload this method is called to apply it to the current scope. /// This method should be called even if the type name was not found in which case a null should be passed in. /// The method validates that some type will be available as the current entity type after it returns (if we are parsing using metadata). /// </summary> /// <param name="entityTypeNameFromPayload">The entity type name found in the payload or null if no type was specified in the payload.</param> protected void ApplyEntityTypeNameFromPayload(string entityTypeNameFromPayload) { Debug.Assert( this.scopes.Count > 0 && this.scopes.Peek().Item is ODataEntry, "Entity type can be applied only when in entry scope."); SerializationTypeNameAnnotation serializationTypeNameAnnotation; EdmTypeKind targetTypeKind; IEdmEntityTypeReference targetEntityTypeReference = (IEdmEntityTypeReference)ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType( EdmTypeKind.Entity, /*defaultPrimitivePayloadType*/ null, this.CurrentEntityType.ToTypeReference(), entityTypeNameFromPayload, this.inputContext.Model, this.inputContext.MessageReaderSettings, this.inputContext.Version, () => EdmTypeKind.Entity, out targetTypeKind, out serializationTypeNameAnnotation); IEdmEntityType targetEntityType = null; ODataEntry entry = this.CurrentEntry; if (targetEntityTypeReference != null) { targetEntityType = targetEntityTypeReference.EntityDefinition(); entry.TypeName = targetEntityType.ODataFullName(); if (serializationTypeNameAnnotation != null) { entry.SetAnnotation(serializationTypeNameAnnotation); } } else if (entityTypeNameFromPayload != null) { entry.TypeName = entityTypeNameFromPayload; } // Set the current entity type since the type from payload might be more derived than // the expected one. this.CurrentEntityType = targetEntityType; }
public void FlagsEnumAsEntityProperty_EmptyStrAsValue_NullAsTypeName() { DateTimeOffset updatedTime = DateTimeOffset.UtcNow; Func<ODataEntry> entryClone = () => { var tmp = new ODataEntry { TypeName = "NS.MyEntityType", Properties = new[] { new ODataProperty { Name = "ColorFlags", Value = new ODataEnumValue("") } }, SerializationInfo = MySerializationInfo }; tmp.SetAnnotation(new AtomEntryMetadata() { Updated = updatedTime }); return tmp; }; // model-request string expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" " + "xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\" m:context=\"http://odata.org/test/$metadata#MySet/$entity\">" + "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" + "<id />" + "<title />" + "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" + "<author><name /></author>" + "<content type=\"application/xml\">" + "<m:properties>" + "<d:ColorFlags m:type=\"#NS.ColorFlags\"></d:ColorFlags>" + "</m:properties>" + "</content>" + "</entry>"; this.WriteRequestWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); // model-reseponse this.WriteResponseWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); // NoModel-request expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" " + "xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\" m:context=\"http://odata.org/test/$metadata#MySet/$entity\">" + "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" + "<id />" + "<title />" + "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" + "<author><name /></author>" + "<content type=\"application/xml\">" + "<m:properties>" + "<d:ColorFlags></d:ColorFlags>" + "</m:properties>" + "</content>" + "</entry>"; this.WriteRequestWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); // NoModel-response this.WriteResponseWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); }
/// <summary> /// Creates an ODataEntry for the given EntityDescriptor and fills in its ODataLib metadata. /// </summary> /// <param name="entityDescriptor">The entity descriptor.</param> /// <param name="serverTypeName">Name of the server type.</param> /// <param name="entityType">The client-side entity type.</param> /// <param name="clientFormat">The current client format.</param> /// <returns>An odata entry with its metadata filled in.</returns> internal static ODataEntry CreateODataEntry(EntityDescriptor entityDescriptor, string serverTypeName, ClientTypeAnnotation entityType, DataServiceClientFormat clientFormat) { ODataEntry entry = new ODataEntry(); // If the client type name is different from the server type name, then add SerializationTypeNameAnnotation // which tells ODataLib to write the type name in the annotation in the payload. if (entityType.ElementTypeName != serverTypeName) { entry.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = serverTypeName }); } // We always need to write the client type name, since this is the type name used by ODataLib // to resolve the entity type using EdmModel.FindSchemaElement. entry.TypeName = entityType.ElementTypeName; // Continue to send the entry's ID in update payloads in Atom for compatibility with V1-V3, // but for JSON-Light we do not want the extra information on the wire. if (clientFormat.UsingAtom && EntityStates.Modified == entityDescriptor.State) { // <id>http://host/service/entityset(key)</id> entry.Id = entityDescriptor.GetLatestIdentity(); } if (entityDescriptor.IsMediaLinkEntry || entityType.IsMediaLinkEntry) { // Since we are already enabled EnableWcfDataServicesClientBehavior in the writer settings, // setting the MediaResource value will tell ODataLib to write MLE payload, irrespective of // what the metadata says. entry.MediaResource = new ODataStreamReferenceValue(); } return entry; }
public void FlagsEnumAsCollectionElement_StrAsValue_StrAsTypeName() { DateTimeOffset updatedTime = DateTimeOffset.UtcNow; Func<ODataEntry> entryClone = () => { var tmp = new ODataEntry { TypeName = "NS.MyEntityType", Properties = new[] { new ODataProperty{Name = "FloatId", Value = new ODataPrimitiveValue(12.3D)}, new ODataProperty{Name = "Color", Value = new ODataEnumValue(Color.Green.ToString(), "NS.Color")}, new ODataProperty { Name = "MyCollectionType", Value = new ODataCollectionValue { Items = new[] { new ODataEnumValue(Color.Red.ToString(),"NS.EnumUndefinedTypename"), new ODataEnumValue(Color.Green.ToString(),"NS.EnumUndefinedTypename")} } } }, SerializationInfo = MySerializationInfo }; tmp.SetAnnotation(new AtomEntryMetadata() { Updated = updatedTime }); return tmp; }; // model-request string expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\" m:context=\"http://odata.org/test/$metadata#MySet/$entity\">" + "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" + "<id />" + "<title />" + "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" + "<author>" + "<name />" + "</author>" + "<content type=\"application/xml\">" + "<m:properties>" + "<d:FloatId m:type=\"Double\">12.3</d:FloatId>" + "<d:Color m:type=\"#NS.Color\">Green</d:Color>" + "<d:MyCollectionType m:type=\"#Collection(NS.ColorFlags)\">" + "<m:element m:type=\"#NS.EnumUndefinedTypename\">Red</m:element>" + "<m:element m:type=\"#NS.EnumUndefinedTypename\">Green</m:element>" + "</d:MyCollectionType>" + "</m:properties>" + "</content>" + "</entry>"; this.WriteRequestWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); // model-reseponse this.WriteResponseWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); // NoModel-request expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\" m:context=\"http://odata.org/test/$metadata#MySet/$entity\">" + "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" + "<id />" + "<title />" + "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" + "<author>" + "<name />" + "</author>" + "<content type=\"application/xml\">" + "<m:properties>" + "<d:FloatId m:type=\"Double\">12.3</d:FloatId>" + "<d:Color m:type=\"#NS.Color\">Green</d:Color>" + "<d:MyCollectionType>" + "<m:element m:type=\"#NS.EnumUndefinedTypename\">Red</m:element>" + "<m:element m:type=\"#NS.EnumUndefinedTypename\">Green</m:element>" + "</d:MyCollectionType>" + "</m:properties>" + "</content>" + "</entry>"; this.WriteRequestWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); // NoModel-response this.WriteResponseWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); }
public void FlagsEnumAsEntityProperty_NullAsValue_ButNonNullable_GetNullError() { DateTimeOffset updatedTime = DateTimeOffset.UtcNow; Func<ODataEntry> entryClone = () => { var tmp = new ODataEntry { TypeName = "NS.MyEntityType", Properties = new[] { new ODataProperty { Name = "ColorFlags", Value = null } }, SerializationInfo = MySerializationInfo }; tmp.SetAnnotation(new AtomEntryMetadata() { Updated = updatedTime }); return tmp; }; string fullName = this.entityType.FindProperty("ColorFlags").Type.FullName(); // model-request Action action = () => this.WriteRequestWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: ""); action.ShouldThrow<ODataException>().WithMessage(Strings.WriterValidationUtils_NonNullablePropertiesMustNotHaveNullValue("ColorFlags", fullName)); // model-reseponse action = () => this.WriteResponseWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: ""); action.ShouldThrow<ODataException>().WithMessage(Strings.WriterValidationUtils_NonNullablePropertiesMustNotHaveNullValue("ColorFlags", fullName)); // NoModel-request string expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" " + "xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\" m:context=\"http://odata.org/test/$metadata#MySet/$entity\">" + "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" + "<id />" + "<title />" + "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" + "<author><name /></author>" + "<content type=\"application/xml\">" + "<m:properties>" + "<d:ColorFlags m:null=\"true\" />" + "</m:properties>" + "</content>" + "</entry>"; this.WriteRequestWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); // NoModel-response this.WriteResponseWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload); }
/// <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 }
internal static void AddTypeNameAnnotationAsNeeded(ODataEntry entry, IEdmEntityType odataPathType, ODataMetadataLevel metadataLevel) { // ODataLib normally has the caller decide whether or not to serialize properties by leaving properties // null when values should not be serialized. The TypeName property is different and should always be // provided to ODataLib to enable model validation. A separate annotation is used to decide whether or not // to serialize the type name (a null value prevents serialization). // Note that this annotation should not be used for Atom or JSON verbose formats, as it will interfere with // the correct default behavior for those formats. Contract.Assert(entry != null); // Only add an annotation if we want to override ODataLib's default type name serialization behavior. if (ShouldAddTypeNameAnnotation(metadataLevel)) { string typeName; // Provide the type name to serialize (or null to force it not to serialize). if (ShouldSuppressTypeNameSerialization(entry, odataPathType, metadataLevel)) { typeName = null; } else { typeName = entry.TypeName; } entry.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation { TypeName = typeName }); } }
private static void AddEntryMetadata(EntityInstance payloadElement, ODataEntry entry) { AtomEntryMetadata metadata = null; foreach (XmlTreeAnnotation epmTree in payloadElement.Annotations.OfType<XmlTreeAnnotation>()) { if (epmTree.NamespaceName == TestAtomConstants.AtomNamespace) { if (metadata == null) { metadata = new AtomEntryMetadata(); } string localName = epmTree.LocalName; if (localName == TestAtomConstants.AtomAuthorElementName) { Debug.Assert(!epmTree.IsAttribute); AtomPersonMetadata author = CreateAuthorMetadata(epmTree.Children); List<AtomPersonMetadata> authors; if (metadata.Authors == null) { authors = new List<AtomPersonMetadata>(); metadata.Authors = authors; } else { authors = (List<AtomPersonMetadata>)metadata.Authors; } authors.Add(author); } else if (localName == TestAtomConstants.AtomCategoryElementName) { Debug.Assert(!epmTree.IsAttribute); AtomCategoryMetadata category = CreateCategoryMetadata(epmTree.Children); List<AtomCategoryMetadata> categories; if (metadata.Categories == null) { categories = new List<AtomCategoryMetadata>(); metadata.Categories = categories; } else { categories = (List<AtomCategoryMetadata>)metadata.Categories; } categories.Add(category); } else if (localName == TestAtomConstants.AtomContributorElementName) { Debug.Assert(!epmTree.IsAttribute); AtomPersonMetadata contributor = CreateAuthorMetadata(epmTree.Children); List<AtomPersonMetadata> contributors; if (metadata.Contributors == null) { contributors = new List<AtomPersonMetadata>(); metadata.Contributors = contributors; } else { contributors = (List<AtomPersonMetadata>)metadata.Contributors; } contributors.Add(contributor); } else if (localName == TestAtomConstants.AtomIdElementName) { Debug.Assert(!epmTree.IsAttribute); entry.Id = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue); } else if (localName == TestAtomConstants.AtomLinkElementName) { Debug.Assert(!epmTree.IsAttribute); AtomLinkMetadata link = CreateLinkMetadata(epmTree.Children); List<AtomLinkMetadata> links; if (metadata.Links == null) { links = new List<AtomLinkMetadata>(); metadata.Links = links; } else { links = (List<AtomLinkMetadata>)metadata.Links; } links.Add(link); } else if (localName == TestAtomConstants.AtomPublishedElementName) { Debug.Assert(!epmTree.IsAttribute); metadata.Published = string.IsNullOrEmpty(epmTree.PropertyValue) ? (DateTimeOffset?)null : DateTimeOffset.Parse(epmTree.PropertyValue); } else if (localName == TestAtomConstants.AtomRightsElementName) { Debug.Assert(!epmTree.IsAttribute); AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children); metadata.Rights = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue }; } else if (localName == TestAtomConstants.AtomSourceElementName) { Debug.Assert(!epmTree.IsAttribute); metadata.Source = CreateFeedMetadata(epmTree.Children, null); } else if (localName == TestAtomConstants.AtomSummaryElementName) { Debug.Assert(!epmTree.IsAttribute); AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children); metadata.Summary = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue }; } else if (localName == TestAtomConstants.AtomTitleElementName) { Debug.Assert(!epmTree.IsAttribute); AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children); metadata.Title = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue }; } else if (localName == TestAtomConstants.AtomUpdatedElementName) { Debug.Assert(!epmTree.IsAttribute); metadata.Updated = string.IsNullOrEmpty(epmTree.PropertyValue) ? (DateTimeOffset?)null : DateTimeOffset.Parse(epmTree.PropertyValue); } else { throw new NotSupportedException("Unsupported atom metadata '" + localName + "' found for entry!"); } } } // Fix up metadata for baselining metadata = metadata.Fixup(); if (metadata != null) { entry.SetAnnotation<AtomEntryMetadata>(metadata); } }
/// <summary> /// Creates and returns an ODataEntry from the given value. /// </summary> /// <param name="entityType">The value type.</param> /// <param name="value">The entry value.</param> /// <param name="properties">The given properties to serialize.</param> /// <returns>An ODataEntry representing the given value.</returns> internal ODataEntry CreateODataEntry(Type entityType, object value, params ClientPropertyAnnotation[] properties) { Debug.Assert(entityType != null, "entityType != null"); Debug.Assert(value != null, "value != null"); ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation entityTypeAnnotation = model.GetClientTypeAnnotation(entityType); Debug.Assert(entityTypeAnnotation != null, "entityTypeAnnotation != null"); Debug.Assert(entityTypeAnnotation.IsEntityType, "Unexpected type"); ODataEntry odataEntityValue = new ODataEntry() { TypeName = entityTypeAnnotation.ElementTypeName, }; string serverTypeName = this.requestInfo.GetServerTypeName(entityTypeAnnotation); odataEntityValue.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = serverTypeName }); odataEntityValue.Properties = this.PopulateProperties(value, serverTypeName, properties.Any() ? properties : entityTypeAnnotation.PropertiesToSerialize(), null); return odataEntityValue; }
internal static void AddTypeNameAnnotationAsNeeded(ODataEntry entry, IEdmEntityType odataPathType, ODataMetadataLevel metadataLevel) { // ODataLib normally has the caller decide whether or not to serialize properties by leaving properties // null when values should not be serialized. The TypeName property is different and should always be // provided to ODataLib to enable model validation. A separate annotation is used to decide whether or not // to serialize the type name (a null value prevents serialization). // Note: In the current version of ODataLib the default behavior likely now matches the requirements for // minimal metadata mode. However, there have been behavior changes/bugs there in the past, so the safer // option is for this class to take control of type name serialization in minimal metadata mode. Contract.Assert(entry != null); string typeName = null; // Set null to force the type name not to serialize. // Provide the type name to serialize. if (!ShouldSuppressTypeNameSerialization(entry, odataPathType, metadataLevel)) { typeName = entry.TypeName; } entry.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation { TypeName = typeName }); }
/// <summary> /// Creates an ODataEntry instance with the default values for 'Id', 'ReadLink' and 'Updated' /// that can be used and modified in tests. /// </summary> /// <param name="typeName">The optional type name for the default entry.</param> /// <param name="model">The product model to generate the type in (if not null).</param> /// <returns>The newly created ODataEntry instance.</returns> public static ODataEntry CreateDefaultEntryWithAtomMetadata(string entitySetName = null, string typeName = null, EdmModel model = null) { if (model != null && typeName != null) { EdmEntityType entityType = new EdmEntityType(DefaultNamespaceName, typeName); entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(isNullable: false))); model.AddElement(entityType); typeName = entityType.FullName(); EdmEntityContainer container = new EdmEntityContainer(DefaultNamespaceName, "DefaultContainer"); model.AddElement(container); if (entitySetName != null) { container.AddEntitySet(entitySetName, entityType); } } ODataEntry entry = new ODataEntry() { Id = DefaultEntryId, ReadLink = DefaultEntryReadLink, TypeName = typeName, SerializationInfo = MySerializationInfo }; AtomEntryMetadata metadata = new AtomEntryMetadata() { Updated = DateTimeOffset.Parse(DefaultEntryUpdated) }; entry.SetAnnotation<AtomEntryMetadata>(metadata); return entry; }
private IEnumerable<ODataItem> CreatePayload(ProjectedPropertiesTestCase testCase) { // First create the entry itself (it might get wrapped later) ODataEntry entry = new ODataEntry() { TypeName = "TestModel.EntityType", Properties = new List<ODataProperty>() { new ODataProperty { Name = "StringProperty", Value = "foo" }, new ODataProperty { Name = "NumberProperty", Value = 42 }, new ODataProperty { Name = "SimpleComplexProperty", Value = new ODataComplexValue { TypeName = "TestModel.SimplexComplexType", Properties = new ODataProperty[] { new ODataProperty { Name = "Name", Value = "Bart" } } } }, new ODataProperty { Name = "DeepComplexProperty", Value = new ODataComplexValue { TypeName = "TestModel.NestedComplexType", Properties = new ODataProperty[] { new ODataProperty { Name = "InnerComplexProperty", Value = new ODataComplexValue { TypeName = "TestModel.SimplexComplexType2", Properties = new ODataProperty[] { new ODataProperty { Name = "Value", Value = 43 } } } } } } }, new ODataProperty { Name = "PrimitiveCollection", Value = new ODataCollectionValue { TypeName = "Collection(Edm.String)", Items = new object[] { "Simpson" } } }, new ODataProperty { Name = "ComplexCollection", Value = new ODataCollectionValue { TypeName = "Collection(TestModel.RatingComplexType)", Items = new object[] { new ODataComplexValue { TypeName = "TestModel.RatingComplexType", Properties = new ODataProperty[] { new ODataProperty { Name = "Rating", Value = -3 } } } } } } }, SerializationInfo = MySerializationInfo }; if (testCase.ResponseOnly) { // Add a stream property for responses ((List<ODataProperty>)entry.Properties).Add(new ODataProperty { Name = "NamedStream", Value = new ODataStreamReferenceValue { EditLink = new Uri("http://odata.org/namedstream") } }); } ODataItem[] entryItems = new ODataItem[] { entry, new ODataNavigationLink { Name = "DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred"), AssociationLinkUrl = testCase.ResponseOnly ? new Uri("http://odata.org/associationlink2") : null }, null, // End deferred link new ODataNavigationLink { Name = "ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/entry") }, new ODataEntry() { TypeName = "TestModel.ExpandedEntryType", Properties = new ODataProperty[] { new ODataProperty { Name = "ExpandedEntryName", Value = "bar" } }, SerializationInfo = MySerializationInfo }, new ODataNavigationLink { Name = "ExpandedEntry_DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred") }, null, // End deffered link new ODataNavigationLink { Name = "ExpandedEntry_ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed") }, new ODataFeed { Id = new Uri("http://test/feedid1"), SerializationInfo = MySerializationInfo }, null, // End feed null, // End exanded expanded feed link null, // End expanded entry null, // End expanded entry nav link new ODataNavigationLink { Name = "ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed") }, new ODataFeed { Id = new Uri("http://test/feedid2") }, new ODataEntry { TypeName = "TestModel.EntityType" }, null, // End entry new ODataEntry { TypeName = "TestModel.EntityType", SerializationInfo = MySerializationInfo }, null, // End entry null, // End expanded feed null, // End expanded feed nav link null, // End the top-level entry }; ProjectedPropertiesAnnotation projectedProperties = testCase.TopLevelProjectedProperties; if (!testCase.NestedPayload) { this.Assert.IsNull(testCase.NestedProjectedProperties, "For a non-nested payload, no nested annotation must be specified."); entry.SetAnnotation(projectedProperties); return entryItems; } // If we are processing a test case for a nested payload, wrap the entry items into a wrapping entry with an expanded navigation link. ODataEntry wrappingEntry = new ODataEntry() { TypeName = "TestModel.WrappingEntityType", Properties = new[] { new ODataProperty { Name = "Wrapping_ID", Value = 1 } }, SerializationInfo = MySerializationInfo }; IEnumerable<ODataItem> wrappedItems = new ODataItem[] { wrappingEntry, new ODataNavigationLink { Name = "Wrapping_ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/wrapping") }} .Concat(entryItems) .Concat(new ODataItem[] { null, null }); ProjectedPropertiesAnnotation nestedProjectedProperties = testCase.NestedProjectedProperties; entry.SetAnnotation(nestedProjectedProperties); wrappingEntry.SetAnnotation(projectedProperties); return wrappedItems; }
private static void WriteEntry(ODataWriter writer, object entity, IEnumerable<string> projectedProperties) { var entry = new ODataEntry() { Id = new Uri("http://temp.org/" + Guid.NewGuid()), SerializationInfo = MySerializationInfo }; if (projectedProperties != null) { entry.SetAnnotation<ProjectedPropertiesAnnotation>(new ProjectedPropertiesAnnotation(projectedProperties)); } entry.Properties = entity.GetType().GetProperties().Select(p => new ODataProperty() { Name = p.Name, Value = p.GetValue(entity, null) }); writer.WriteStart(entry); writer.WriteEnd(); }
/// <summary> /// Creates the materializer entry. /// </summary> /// <param name="entry">The entry.</param> /// <param name="format">The format the entry was read in.</param> /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param> /// <param name="model">The client model.</param> /// <returns>A new materializer entry.</returns> public static MaterializerEntry CreateEntry(ODataEntry entry, ODataFormat format, bool isTracking, ClientEdmModel model) { Debug.Assert(entry.GetAnnotation<MaterializerEntry>() == null, "MaterializerEntry has already been created."); MaterializerEntry materializerEntry = new MaterializerEntry(entry, format, isTracking, model); entry.SetAnnotation<MaterializerEntry>(materializerEntry); return materializerEntry; }