/// <summary> /// Appends the key expression for the given entity to the given <see cref="StringBuilder"/> /// </summary> /// <typeparam name="T">The type of the properties.</typeparam> /// <param name="keyProperties">The properties of the key.</param> /// <param name="getPropertyName">Delegate to get the name of a property.</param> /// <param name="getValueForProperty">Delegate to get the value of a property.</param> /// <param name="builder">The builder to append onto.</param> internal void AppendKeyExpression <T>(ICollection <T> keyProperties, Func <T, string> getPropertyName, Func <T, object> getValueForProperty, StringBuilder builder) { Func <T, object> getValueForPropertyWithNullCheck = p => { Debug.Assert(getValueForProperty != null, "getValueForProperty != null"); object propertyValue = getValueForProperty(p); if (propertyValue == null) { throw Error.InvalidOperation(Client.Strings.Context_NullKeysAreNotSupported(getPropertyName(p))); } return(propertyValue); }; this.keySerializer.AppendKeyExpression(builder, keyProperties, getPropertyName, getValueForPropertyWithNullCheck); }
/// <summary> /// Gets the raw CLR value for the given <see cref="IEdmPropertyValue"/>. /// </summary> /// <param name="property">The property to get the value for.</param> /// <param name="type">The type which declared the property.</param> /// <returns>The raw CLR value of the property.</returns> private static object GetPropertyValue(IEdmPropertyValue property, IEdmTypeReference type) { Debug.Assert(property != null, "property != null"); IEdmValue propertyValue = property.Value; // DEVNOTE: though this check is not strictly necessary, and would be caught by later checks, // it seems worthwhile to fail fast if we can. if (propertyValue.ValueKind == EdmValueKind.Null) { throw Error.InvalidOperation(ErrorStrings.Context_NullKeysAreNotSupported(property.Name)); } var primitiveValue = propertyValue as IEdmPrimitiveValue; if (primitiveValue == null) { throw Error.InvalidOperation(ErrorStrings.ClientType_KeysMustBeSimpleTypes(type.FullName())); } // DEVNOTE: This can return null, and will be handled later. The reason for this is that the client // and server have different ways of getting property values, but both will eventually hit the same // codepath and that is where the null is handled. return(primitiveValue.ToClrValue()); }
/// <summary> /// discover and prepare properties for usage /// </summary> /// <param name="type">type being processed</param> /// <param name="typeName">parameter name</param> /// <param name="skipSettableCheck">Whether the skip the check for settable properties.</param> private ClientType(Type type, string typeName, bool skipSettableCheck) { Debug.Assert(null != type, "null type"); Debug.Assert(!String.IsNullOrEmpty(typeName), "empty typeName"); this.ElementTypeName = typeName; this.ElementType = Nullable.GetUnderlyingType(type) ?? type; #if ASTORIA_OPEN_OBJECT string openObjectPropertyName = null; #endif if (!ClientConvert.IsKnownType(this.ElementType)) { #if ASTORIA_OPEN_OBJECT #region OpenObject determined by walking type hierarchy and looking for [OpenObjectAttribute("PropertyName")] Type openObjectDeclared = this.ElementType; for (Type tmp = openObjectDeclared; (null != tmp) && (typeof(object) != tmp); tmp = tmp.BaseType) { object[] attributes = openObjectDeclared.GetCustomAttributes(typeof(OpenObjectAttribute), false); if (1 == attributes.Length) { if (null != openObjectPropertyName) { throw Error.InvalidOperation(Strings.Clienttype_MultipleOpenProperty(this.ElementTypeName)); } openObjectPropertyName = ((OpenObjectAttribute)attributes[0]).OpenObjectPropertyName; openObjectDeclared = tmp; } } #endregion #endif Type keyPropertyDeclaredType = null; bool isEntity = type.GetCustomAttributes(true).OfType <DataServiceEntityAttribute>().Any(); DataServiceKeyAttribute dska = type.GetCustomAttributes(true).OfType <DataServiceKeyAttribute>().FirstOrDefault(); foreach (PropertyInfo pinfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { //// examples where class<PropertyType> //// the normal examples //// PropertyType Property { get; set } //// Nullable<PropertyType> Property { get; set; } //// if 'Property: struct' then we would be unable set the property during construction (and have them stick) //// but when its a class, we can navigate if non-null and set the nested properties //// PropertyType Property { get; } where PropertyType: class //// we do support adding elements to collections //// ICollection<PropertyType> { get; /*ignored set;*/ } //// indexed properties are not suported because //// we don't have anything to use as the index //// PropertyType Property[object x] { /*ignored get;*/ /*ignored set;*/ } //// also ignored //// if PropertyType.IsPointer (like byte*) //// if PropertyType.IsArray except for byte[] and char[] //// if PropertyType == IntPtr or UIntPtr Type ptype = pinfo.PropertyType; // class / interface / value ptype = Nullable.GetUnderlyingType(ptype) ?? ptype; if (ptype.IsPointer || (ptype.IsArray && (typeof(byte[]) != ptype) && typeof(char[]) != ptype) || (typeof(IntPtr) == ptype) || (typeof(UIntPtr) == ptype)) { continue; } Debug.Assert(!ptype.ContainsGenericParameters, "remove when test case is found that encounters this"); if (pinfo.CanRead && (!ptype.IsValueType || pinfo.CanWrite) && !ptype.ContainsGenericParameters && (0 == pinfo.GetIndexParameters().Length)) { #region IsKey? bool keyProperty = dska != null?dska.KeyNames.Contains(pinfo.Name) : false; if (keyProperty) { if (null == keyPropertyDeclaredType) { keyPropertyDeclaredType = pinfo.DeclaringType; } else if (keyPropertyDeclaredType != pinfo.DeclaringType) { throw Error.InvalidOperation(Strings.ClientType_KeysOnDifferentDeclaredType(this.ElementTypeName)); } if (!ClientConvert.IsKnownType(ptype)) { throw Error.InvalidOperation(Strings.ClientType_KeysMustBeSimpleTypes(this.ElementTypeName)); } this.KeyCount++; } #endregion #if ASTORIA_OPEN_OBJECT #region IsOpenObjectProperty? bool openProperty = (openObjectPropertyName == pinfo.Name) && typeof(IDictionary <string, object>).IsAssignableFrom(ptype); Debug.Assert(keyProperty != openProperty || (!keyProperty && !openProperty), "key can't be open type"); #endregion ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty, openProperty); if (!property.OpenObjectProperty) #else ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty); #endif { if (!this.properties.Add(property, ClientProperty.NameEquality)) { // 2nd property with same name shadows another property int shadow = this.IndexOfProperty(property.PropertyName); if (!property.DeclaringType.IsAssignableFrom(this.properties[shadow].DeclaringType)) { // the new property is on the most derived class this.properties.RemoveAt(shadow); this.properties.Add(property, null); } } } #if ASTORIA_OPEN_OBJECT else { if (pinfo.DeclaringType == openObjectDeclared) { this.openProperties = property; } } #endif } } #region No KeyAttribute, discover key by name pattern { DeclaringType.Name+ID, ID } if (null == keyPropertyDeclaredType) { ClientProperty key = null; for (int i = this.properties.Count - 1; 0 <= i; --i) { string propertyName = this.properties[i].PropertyName; if (propertyName.EndsWith("ID", StringComparison.Ordinal)) { string declaringTypeName = this.properties[i].DeclaringType.Name; if ((propertyName.Length == (declaringTypeName.Length + 2)) && propertyName.StartsWith(declaringTypeName, StringComparison.Ordinal)) { // matched "DeclaringType.Name+ID" pattern if ((null == keyPropertyDeclaredType) || this.properties[i].DeclaringType.IsAssignableFrom(keyPropertyDeclaredType)) { keyPropertyDeclaredType = this.properties[i].DeclaringType; key = this.properties[i]; } } else if ((null == keyPropertyDeclaredType) && (2 == propertyName.Length)) { // matched "ID" pattern keyPropertyDeclaredType = this.properties[i].DeclaringType; key = this.properties[i]; } } } if (null != key) { Debug.Assert(0 == this.KeyCount, "shouldn't have a key yet"); key.KeyProperty = true; this.KeyCount++; } } else if (this.KeyCount != dska.KeyNames.Count) { var m = (from string a in dska.KeyNames where null == (from b in this.properties where b.PropertyName == a select b).FirstOrDefault() select a).First <string>(); throw Error.InvalidOperation(Strings.ClientType_MissingProperty(this.ElementTypeName, m)); } #endregion this.IsEntityType = (null != keyPropertyDeclaredType) || isEntity; Debug.Assert(this.KeyCount == this.Properties.Where(k => k.KeyProperty).Count(), "KeyCount mismatch"); this.WireUpMimeTypeProperties(); this.CheckMediaLinkEntry(); if (!skipSettableCheck) { #if ASTORIA_OPEN_OBJECT if ((0 == this.properties.Count) && (null == this.openProperties)) #else if (0 == this.properties.Count) #endif { // implicit construction? throw Error.InvalidOperation(Strings.ClientType_NoSettableFields(this.ElementTypeName)); } } }
private ClientType(Type type, string typeName, bool skipSettableCheck) { Debug.Assert(null != type, "null type"); Debug.Assert(!String.IsNullOrEmpty(typeName), "empty typeName"); this.ElementTypeName = typeName; this.ElementType = Nullable.GetUnderlyingType(type) ?? type; #if ASTORIA_OPEN_OBJECT string openObjectPropertyName = null; #endif if (!ClientConvert.IsKnownType(this.ElementType)) { #if ASTORIA_OPEN_OBJECT #region OpenObject determined by walking type hierarchy and looking for [OpenObjectAttribute("PropertyName")] Type openObjectDeclared = this.ElementType; for (Type tmp = openObjectDeclared; (null != tmp) && (typeof(object) != tmp); tmp = tmp.BaseType) { object[] attributes = openObjectDeclared.GetCustomAttributes(typeof(OpenObjectAttribute), false); if (1 == attributes.Length) { if (null != openObjectPropertyName) { throw Error.InvalidOperation(Strings.Clienttype_MultipleOpenProperty(this.ElementTypeName)); } openObjectPropertyName = ((OpenObjectAttribute)attributes[0]).OpenObjectPropertyName; openObjectDeclared = tmp; } } #endregion #endif Type keyPropertyDeclaredType = null; bool isEntity = type.GetCustomAttributes(true).OfType <DataServiceEntityAttribute>().Any(); DataServiceKeyAttribute dska = type.GetCustomAttributes(true).OfType <DataServiceKeyAttribute>().FirstOrDefault(); foreach (PropertyInfo pinfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { Type ptype = pinfo.PropertyType; ptype = Nullable.GetUnderlyingType(ptype) ?? ptype; if (ptype.IsPointer || (ptype.IsArray && (typeof(byte[]) != ptype) && typeof(char[]) != ptype) || (typeof(IntPtr) == ptype) || (typeof(UIntPtr) == ptype)) { continue; } Debug.Assert(!ptype.ContainsGenericParameters, "remove when test case is found that encounters this"); if (pinfo.CanRead && (!ptype.IsValueType || pinfo.CanWrite) && !ptype.ContainsGenericParameters && (0 == pinfo.GetIndexParameters().Length)) { #region IsKey? bool keyProperty = dska != null?dska.KeyNames.Contains(pinfo.Name) : false; if (keyProperty) { if (null == keyPropertyDeclaredType) { keyPropertyDeclaredType = pinfo.DeclaringType; } else if (keyPropertyDeclaredType != pinfo.DeclaringType) { throw Error.InvalidOperation(Strings.ClientType_KeysOnDifferentDeclaredType(this.ElementTypeName)); } if (!ClientConvert.IsKnownType(ptype)) { throw Error.InvalidOperation(Strings.ClientType_KeysMustBeSimpleTypes(this.ElementTypeName)); } this.KeyCount++; } #endregion #if ASTORIA_OPEN_OBJECT #region IsOpenObjectProperty? bool openProperty = (openObjectPropertyName == pinfo.Name) && typeof(IDictionary <string, object>).IsAssignableFrom(ptype); Debug.Assert(keyProperty != openProperty || (!keyProperty && !openProperty), "key can't be open type"); #endregion ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty, openProperty); if (!property.OpenObjectProperty) #else ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty); #endif { if (!this.properties.Add(property, ClientProperty.NameEquality)) { int shadow = this.IndexOfProperty(property.PropertyName); if (!property.DeclaringType.IsAssignableFrom(this.properties[shadow].DeclaringType)) { this.properties.RemoveAt(shadow); this.properties.Add(property, null); } } } #if ASTORIA_OPEN_OBJECT else { if (pinfo.DeclaringType == openObjectDeclared) { this.openProperties = property; } } #endif } } #region No KeyAttribute, discover key by name pattern { DeclaringType.Name+ID, ID } if (null == keyPropertyDeclaredType) { ClientProperty key = null; for (int i = this.properties.Count - 1; 0 <= i; --i) { string propertyName = this.properties[i].PropertyName; if (propertyName.EndsWith("ID", StringComparison.Ordinal)) { string declaringTypeName = this.properties[i].DeclaringType.Name; if ((propertyName.Length == (declaringTypeName.Length + 2)) && propertyName.StartsWith(declaringTypeName, StringComparison.Ordinal)) { if ((null == keyPropertyDeclaredType) || this.properties[i].DeclaringType.IsAssignableFrom(keyPropertyDeclaredType)) { keyPropertyDeclaredType = this.properties[i].DeclaringType; key = this.properties[i]; } } else if ((null == keyPropertyDeclaredType) && (2 == propertyName.Length)) { keyPropertyDeclaredType = this.properties[i].DeclaringType; key = this.properties[i]; } } } if (null != key) { Debug.Assert(0 == this.KeyCount, "shouldn't have a key yet"); key.KeyProperty = true; this.KeyCount++; } } else if (this.KeyCount != dska.KeyNames.Count) { var m = (from string a in dska.KeyNames where null == (from b in this.properties where b.PropertyName == a select b).FirstOrDefault() select a).First <string>(); throw Error.InvalidOperation(Strings.ClientType_MissingProperty(this.ElementTypeName, m)); } #endregion this.IsEntityType = (null != keyPropertyDeclaredType) || isEntity; Debug.Assert(this.KeyCount == this.Properties.Where(k => k.KeyProperty).Count(), "KeyCount mismatch"); this.WireUpMimeTypeProperties(); this.CheckMediaLinkEntry(); if (!skipSettableCheck) { #if ASTORIA_OPEN_OBJECT if ((0 == this.properties.Count) && (null == this.openProperties)) #else if (0 == this.properties.Count) #endif { throw Error.InvalidOperation(Strings.ClientType_NoSettableFields(this.ElementTypeName)); } } }
private string ConvertToEscapedUriValue(string paramName, object value) { Debug.Assert(!string.IsNullOrEmpty(paramName), "!string.IsNullOrEmpty(paramName)"); Object valueInODataFormat = null; // Literal values with single quotes need special escaping due to System.Uri changes in behavior between .NET 4.0 and 4.5. // We need to ensure that our escaped values do not change between those versions, so we need to escape values differently when they could contain single quotes. bool needsSpecialEscaping = false; if (value == null) { needsSpecialEscaping = true; } else { if (value.GetType() == typeof(ODataUriNullValue)) { valueInODataFormat = value; needsSpecialEscaping = true; } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(value.GetType()); Debug.Assert(edmType != null, "edmType != null"); switch (edmType.TypeKind) { case EdmTypeKind.Primitive: valueInODataFormat = value; needsSpecialEscaping = true; break; case EdmTypeKind.Complex: ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = this.propertyConverter.CreateODataComplexValue( typeAnnotation.ElementType, value, null /*propertyName*/, false /*isCollectionItemType*/, null /*visitedComplexTypeObjects*/); // When using JsonVerbose to format query string parameters for Actions, // we cannot write out Complex values in the URI without the type name of the complex type in the JSON payload. // If this value is null, the client has to set the ResolveName property on the DataServiceContext instance. ODataComplexValue complexValue = (ODataComplexValue)valueInODataFormat; SerializationTypeNameAnnotation serializedTypeNameAnnotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializedTypeNameAnnotation == null || string.IsNullOrEmpty(serializedTypeNameAnnotation.TypeName)) { throw Error.InvalidOperation(Strings.DataServiceException_GeneralError); } break; case EdmTypeKind.Collection: IEdmCollectionType edmCollectionType = edmType as IEdmCollectionType; Debug.Assert(edmCollectionType != null, "edmCollectionType != null"); IEdmTypeReference itemTypeReference = edmCollectionType.ElementType; Debug.Assert(itemTypeReference != null, "itemTypeReference != null"); ClientTypeAnnotation itemTypeAnnotation = model.GetClientTypeAnnotation(itemTypeReference.Definition); Debug.Assert(itemTypeAnnotation != null, "itemTypeAnnotation != null"); switch (itemTypeAnnotation.EdmType.TypeKind) { // We only support primitive or complex type as a collection item type. case EdmTypeKind.Primitive: case EdmTypeKind.Complex: break; default: throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(paramName, itemTypeAnnotation.EdmType.TypeKind)); } valueInODataFormat = this.propertyConverter.CreateODataCollection( itemTypeAnnotation.ElementType, null /*propertyName*/, value, null /*visitedComplexTypeObjects*/); break; default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference // EdmTypeKind.Enum. throw new NotSupportedException(Strings.Serializer_InvalidParameterType(paramName, edmType.TypeKind)); } } Debug.Assert(valueInODataFormat != null, "valueInODataFormat != null"); } // In the released WCF Data Services 5.0, we didn't pass the model for JSON Verbose literals, so continuing that behavior for backward compatibility ODataFormat literalFormat = this.requestInfo.Format.UriLiteralFormat; IEdmModel edmModel = literalFormat == ODataFormat.VerboseJson ? null : this.requestInfo.Model; // ODL can handle null values so we can send null values here. string literal = ODataUriUtils.ConvertToUriLiteral(valueInODataFormat, CommonUtil.ConvertToODataVersion(this.requestInfo.MaxProtocolVersionAsVersion), edmModel, literalFormat); // The value from ConvertToUriValue will not be escaped, but will already contain literal delimiters like single quotes, so we // need to use our own escape method that will preserve those characters instead of directly calling Uri.EscapeDataString that may escape them. // This is only necessary for primitives and nulls because the other structures are serialized using the JSON format and it uses double quotes // which have always been escaped. if (needsSpecialEscaping) { return(DataStringEscapeBuilder.EscapeDataString(literal)); } return(Uri.EscapeDataString(literal)); }
/// <summary> /// Parses the current reader into a new <paramref name="targetEntry"/> /// instance. /// </summary> /// <param name="targetEntry"> /// After invocation, the target entry that was created as a result /// of parsing the current reader. /// </param> private void ParseCurrentEntry(out AtomEntry targetEntry) { Debug.Assert(this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element"); // Push reader. var callbackResult = this.entryCallback(this.reader); Debug.Assert(callbackResult.Key != null, "callbackResult.Key != null"); this.readers.Push(this.reader); this.reader = callbackResult.Key; this.reader.Read(); Debug.Assert(this.reader.LocalName == "entry", "this.reader.LocalName == 'entry' - otherwise we're not reading the subtree"); bool hasContent = false; targetEntry = new AtomEntry(); targetEntry.DataValues = new List <AtomContentProperty>(); targetEntry.Tag = callbackResult.Value; targetEntry.ETagText = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace); while (this.reader.Read()) { if (ShouldIgnoreNode(this.reader)) { continue; } if (this.reader.NodeType == XmlNodeType.Element) { int depth = this.reader.Depth; string elementName = this.reader.LocalName; string namespaceURI = this.reader.NamespaceURI; if (namespaceURI == XmlConstants.AtomNamespace) { if (elementName == XmlConstants.AtomCategoryElementName && targetEntry.TypeName == null) { string text = this.reader.GetAttributeEx(XmlConstants.AtomCategorySchemeAttributeName, XmlConstants.AtomNamespace); if (text == this.typeScheme) { targetEntry.TypeName = this.reader.GetAttributeEx(XmlConstants.AtomCategoryTermAttributeName, XmlConstants.AtomNamespace); } } else if (elementName == XmlConstants.AtomContentElementName) { hasContent = true; this.ParseCurrentContent(targetEntry); } else if (elementName == XmlConstants.AtomIdElementName && targetEntry.Identity == null) { // The .Identity == null check ensures that only the first id element is processed. string idText = ReadElementStringForText(this.reader); idText = Util.ReferenceIdentity(idText); // here we could just assign idText to Identity // however we used to check for AbsoluteUri, thus we need to // convert string to Uri and check for absoluteness Uri idUri = Util.CreateUri(idText, UriKind.RelativeOrAbsolute); if (!idUri.IsAbsoluteUri) { throw Error.InvalidOperation(Strings.Context_TrackingExpectsAbsoluteUri); } targetEntry.Identity = idText; } else if (elementName == XmlConstants.AtomLinkElementName) { this.ParseCurrentLink(targetEntry); } } else if (namespaceURI == XmlConstants.DataWebMetadataNamespace) { if (elementName == XmlConstants.AtomPropertiesElementName) { if (targetEntry.MediaLinkEntry.HasValue && !targetEntry.MediaLinkEntry.Value) { // This means we saw a non-empty <atom:Content> element but now we have a Properties element // that also carries properties throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed); } targetEntry.MediaLinkEntry = true; if (!this.reader.IsEmptyElement) { this.ReadCurrentProperties(targetEntry.DataValues); } } } SkipToEndAtDepth(this.reader, depth); } } if (targetEntry.Identity == null) { throw Error.InvalidOperation(Strings.Deserialize_MissingIdElement); } if (!hasContent) { // Content is expected for the GetResponse operation throw Error.BatchStreamContentExpected(BatchStreamState.GetResponse); } this.reader = this.readers.Pop(); }
private void ParseCurrentLink(AtomEntry targetEntry) { Debug.Assert(targetEntry != null, "targetEntry != null"); Debug.Assert( this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element -- otherwise we shouldn't try to parse a link"); Debug.Assert( this.reader.LocalName == "link", "this.reader.LocalName == 'link' -- otherwise we shouldn't try to parse a link"); string relation = this.reader.GetAttribute(XmlConstants.AtomLinkRelationAttributeName); if (relation == null) { return; } if (relation == XmlConstants.AtomEditRelationAttributeValue && targetEntry.EditLink == null) { // Only process the first link that has @rel='edit'. string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName); if (String.IsNullOrEmpty(href)) { throw Error.InvalidOperation(Strings.Context_MissingEditLinkInResponseBody); } targetEntry.EditLink = this.ConvertHRefAttributeValueIntoURI(href); } else if (relation == XmlConstants.AtomSelfRelationAttributeValue && targetEntry.QueryLink == null) { // Only process the first link that has @rel='self'. string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName); if (String.IsNullOrEmpty(href)) { throw Error.InvalidOperation(Strings.Context_MissingSelfLinkInResponseBody); } targetEntry.QueryLink = this.ConvertHRefAttributeValueIntoURI(href); } else if (relation == XmlConstants.AtomEditMediaRelationAttributeValue && targetEntry.MediaEditUri == null) { string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName); if (String.IsNullOrEmpty(href)) { throw Error.InvalidOperation(Strings.Context_MissingEditMediaLinkInResponseBody); } targetEntry.MediaEditUri = this.ConvertHRefAttributeValueIntoURI(href); targetEntry.StreamETagText = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace); } if (!this.reader.IsEmptyElement) { string propertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(relation); if (propertyName == null) { return; } string propertyValueText = this.reader.GetAttribute(XmlConstants.AtomTypeAttributeName); bool isFeed; if (!IsAllowedLinkType(propertyValueText, out isFeed)) { return; } if (!ReadChildElement(this.reader, XmlConstants.AtomInlineElementName, XmlConstants.DataWebMetadataNamespace)) { return; } bool emptyInlineCollection = this.reader.IsEmptyElement; object propertyValue = null; if (!emptyInlineCollection) { AtomFeed nestedFeed = null; AtomEntry nestedEntry = null; List <AtomEntry> feedEntries = null; Debug.Assert(this.reader is Xml.XmlWrappingReader, "reader must be a instance of XmlWrappingReader"); string readerBaseUri = this.reader.BaseURI; XmlReader nestedReader = Xml.XmlWrappingReader.CreateReader(readerBaseUri, this.reader.ReadSubtree()); nestedReader.Read(); Debug.Assert(nestedReader.LocalName == "inline", "nestedReader.LocalName == 'inline'"); AtomParser nested = new AtomParser(nestedReader, this.entryCallback, this.typeScheme, this.currentDataNamespace); while (nested.Read()) { switch (nested.DataKind) { case AtomDataKind.Feed: feedEntries = new List <AtomEntry>(); nestedFeed = nested.CurrentFeed; propertyValue = nestedFeed; break; case AtomDataKind.Entry: nestedEntry = nested.CurrentEntry; if (feedEntries != null) { feedEntries.Add(nestedEntry); } else { propertyValue = nestedEntry; } break; case AtomDataKind.PagingLinks: // Here the inner feed parser found a paging link, and stored it on nestedFeed.NextPageLink // we are going to add it into a link table and associate // with the collection at AtomMaterializer::Materialize() // Do nothing for now. break; default: throw new InvalidOperationException(Strings.AtomParser_UnexpectedContentUnderExpandedLink); } } if (nestedFeed != null) { Debug.Assert( nestedFeed.Entries == null, "nestedFeed.Entries == null -- otherwise someone initialized this for us"); nestedFeed.Entries = feedEntries; } } AtomContentProperty property = new AtomContentProperty(); property.Name = propertyName; if (emptyInlineCollection || propertyValue == null) { property.IsNull = true; if (isFeed) { property.Feed = new AtomFeed(); property.Feed.Entries = Enumerable.Empty <AtomEntry>(); } else { property.Entry = new AtomEntry(); property.Entry.IsNull = true; } } else { property.Feed = propertyValue as AtomFeed; property.Entry = propertyValue as AtomEntry; } targetEntry.DataValues.Add(property); } }
internal static object ChangeType(string propertyValue, Type propertyType) { Debug.Assert(null != propertyValue, "should never be passed null"); try { switch ((StorageType)IndexOfStorage(propertyType)) { case StorageType.Boolean: return(XmlConvert.ToBoolean(propertyValue)); case StorageType.Byte: return(XmlConvert.ToByte(propertyValue)); case StorageType.ByteArray: return(Convert.FromBase64String(propertyValue)); case StorageType.Char: return(XmlConvert.ToChar(propertyValue)); case StorageType.CharArray: return(propertyValue.ToCharArray()); case StorageType.DateTime: return(XmlConvert.ToDateTime(propertyValue, XmlDateTimeSerializationMode.RoundtripKind)); case StorageType.DateTimeOffset: return(XmlConvert.ToDateTimeOffset(propertyValue)); case StorageType.Decimal: return(XmlConvert.ToDecimal(propertyValue)); case StorageType.Double: return(XmlConvert.ToDouble(propertyValue)); case StorageType.Guid: return(new Guid(propertyValue)); case StorageType.Int16: return(XmlConvert.ToInt16(propertyValue)); case StorageType.Int32: return(XmlConvert.ToInt32(propertyValue)); case StorageType.Int64: return(XmlConvert.ToInt64(propertyValue)); case StorageType.Single: return(XmlConvert.ToSingle(propertyValue)); case StorageType.String: return(propertyValue); case StorageType.SByte: return(XmlConvert.ToSByte(propertyValue)); case StorageType.TimeSpan: return(XmlConvert.ToTimeSpan(propertyValue)); case StorageType.Type: return(Type.GetType(propertyValue, true)); case StorageType.UInt16: return(XmlConvert.ToUInt16(propertyValue)); case StorageType.UInt32: return(XmlConvert.ToUInt32(propertyValue)); case StorageType.UInt64: return(XmlConvert.ToUInt64(propertyValue)); case StorageType.Uri: return(Util.CreateUri(propertyValue, UriKind.RelativeOrAbsolute)); case StorageType.XDocument: return(0 < propertyValue.Length ? System.Xml.Linq.XDocument.Parse(propertyValue) : new System.Xml.Linq.XDocument()); case StorageType.XElement: return(System.Xml.Linq.XElement.Parse(propertyValue)); #if !ASTORIA_LIGHT case StorageType.Binary: Debug.Assert(null != knownTypes[(int)StorageType.Binary], "null typeof(System.Data.Linq.Binary)"); return(Activator.CreateInstance(knownTypes[(int)StorageType.Binary], Convert.FromBase64String(propertyValue))); #endif default: Debug.Assert(false, "new StorageType without update to knownTypes"); return(propertyValue); } } catch (FormatException ex) { propertyValue = (0 == propertyValue.Length ? "String.Empty" : "String"); throw Error.InvalidOperation(Strings.Deserialize_Current(propertyType.ToString(), propertyValue), ex); } catch (OverflowException ex) { propertyValue = (0 == propertyValue.Length ? "String.Empty" : "String"); throw Error.InvalidOperation(Strings.Deserialize_Current(propertyType.ToString(), propertyValue), ex); } }