Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / SyndicationDeserializer.cs / 3 / SyndicationDeserializer.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Provides a deserializer for atom content structured as
// syndication items or feeds.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Serializers
{
#region Namespaces.
using System;
using System.Collections.Generic;
using System.Data.Services.Providers;
using System.Diagnostics;
using System.Linq;
using System.IO;
using System.ServiceModel.Syndication;
using System.Text;
using System.Xml;
#endregion Namespaces.
/// Provides a deserializer for structured content.
internal class SyndicationDeserializer : Deserializer
{
/// Factory for syndication formatting objects.
private readonly SyndicationFormatterFactory factory;
/// reader to read xml from the request stream
private readonly XmlReader xmlReader;
/// Initializes a new for the specified stream.
/// Input stream reader from which ATOM content must be read.
/// Encoding to use for the stream (null to auto-discover).
/// Data service for which the deserializer will act.
/// indicates whether this is a update operation or not
/// Factory for formatter objects.
/// Tracker to use for modifications.
internal SyndicationDeserializer(Stream stream, Encoding encoding, IDataService dataService, bool update, SyndicationFormatterFactory factory, UpdateTracker tracker)
: base(update, dataService, tracker)
{
Debug.Assert(stream != null, "stream != null");
Debug.Assert(factory != null, "factory != null");
this.factory = factory;
this.xmlReader = factory.CreateReader(stream, encoding);
}
///
/// Indicates the various form of data in the inline xml element
///
private enum LinkContent
{
/// If the link element didn't not contain an inline element at all.
NoInlineElementSpecified,
/// If the link element contained an empty inline element.
EmptyInlineElementSpecified,
/// If the inline element under the link element contained some data.
InlineElementContainsData
}
/// returns the content format for the deserializer
protected override ContentFormat ContentFormat
{
get
{
return ContentFormat.Atom;
}
}
///
/// Assumes the payload to represent a single object and processes accordingly
///
/// info about the object being created
/// the newly formed object that the payload represents
protected override object CreateSingleObject(SegmentInfo segmentInfo)
{
SyndicationItem item = ReadSyndicationItem(this.factory.CreateSyndicationItemFormatter(), this.xmlReader);
return this.CreateObject(segmentInfo, true /*topLevel*/, item);
}
/// Provides an opportunity to clean-up resources.
///
/// Whether the call is being made from an explicit call to
/// IDisposable.Dispose() rather than through the finalizer.
///
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
this.xmlReader.Close();
}
}
///
/// Get the resource referred by the uri in the payload
///
/// resource referred by the uri in the payload.
protected override string GetLinkUriFromPayload()
{
throw Error.NotImplemented();
}
/// Checks whether the specified item has a payload.
/// Item to check.
/// true if the item has content or links specified; false otherwise.
private static bool HasContent(SyndicationItem item)
{
return item.Content != null || item.Links.Count > 0;
}
/// Gets the text for the type annotated on the specified .
/// Item to read type from.
/// The text for the type annotated on the specified item; null if none is set.
private static string SyndicationItemGetType(SyndicationItem item)
{
Debug.Assert(item != null, "item != null");
SyndicationCategory category = item.Categories.Where(c => c.Scheme == XmlConstants.DataWebSchemeNamespace).FirstOrDefault();
return (category == null) ? null : category.Name;
}
/// Reads a SyndicationFeed object from the specified XmlReader.
/// Formatter to use when reading content.
/// Read to read feed from.
/// A new SyndicationFeed instance.
private static SyndicationFeed ReadSyndicationFeed(SyndicationFeedFormatter formatter, XmlReader reader)
{
Debug.Assert(formatter != null, "formatter != null");
Debug.Assert(reader != null, "reader != null");
try
{
formatter.ReadFrom(reader);
}
catch (XmlException exception)
{
throw DataServiceException.CreateBadRequestError(Strings.Syndication_ErrorReadingFeed(exception.Message), exception);
}
Debug.Assert(formatter.Feed != null, "formatter.Feed != null");
return formatter.Feed;
}
/// Reads a SyndicationItem object from the specified XmlReader.
/// Formatter to use when reading content.
/// Read to read feed from.
/// A new SyndicationItem instance.
private static SyndicationItem ReadSyndicationItem(SyndicationItemFormatter formatter, XmlReader reader)
{
Debug.Assert(formatter != null, "formatter != null");
Debug.Assert(reader != null, "reader != null");
try
{
formatter.ReadFrom(reader);
}
catch (XmlException exception)
{
throw DataServiceException.CreateBadRequestError(Strings.Syndication_ErrorReadingEntry(exception.Message), exception);
}
Debug.Assert(formatter.Item != null, "formatter.Item != null");
return formatter.Item;
}
///
/// Read the link media type and validate for non open property types
///
/// media type as specified on the link element.
/// property which the link represents.
/// returns the type parameters specified in the media link.
private static string ValidateTypeParameterForNonOpenTypeProperties(string mediaType, ResourceProperty property)
{
string typeParameterValue = null;
if (!String.IsNullOrEmpty(mediaType))
{
string mime;
Encoding encoding;
KeyValuePair[] contentTypeParameters = HttpProcessUtility.ReadContentType(mediaType, out mime, out encoding);
if (mime != XmlConstants.MimeApplicationAtom)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MimeTypeMustBeApplicationAtom(mime, XmlConstants.MimeApplicationAtom));
}
// If the type parameter is specified, make sure its correct. We do the validation for known properties here
// and for open-properties, the validation is done if the link is expanded. Otherwise, there is no good way of
// doing the validation.
typeParameterValue = HttpProcessUtility.GetParameterValue(contentTypeParameters, XmlConstants.AtomTypeAttributeName);
if (!String.IsNullOrEmpty(typeParameterValue) && property != null)
{
if (property.Kind == ResourcePropertyKind.ResourceReference)
{
if (typeParameterValue != XmlConstants.AtomEntryElementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomEntryElementName));
}
}
else if (typeParameterValue != XmlConstants.AtomFeedElementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomFeedElementName));
}
}
}
return typeParameterValue;
}
/// Applies the properties in the plain XML content to the specified resource.
/// Content to read values from.
/// Type of resource whose values are being set.
/// Target resource.
private void ApplyContent(XmlSyndicationContent content, ResourceType resourceType, object resource)
{
Debug.Assert(content != null, "content != null");
using (XmlReader reader = content.GetReaderAtContent())
{
WebUtil.XmlReaderEnsureElement(reader);
Debug.Assert(
reader.NodeType == XmlNodeType.Element,
reader.NodeType.ToString() + " == XmlNodeType.Element -- otherwise XmlSyndicationContent didn't see a 'content' tag");
reader.ReadStartElement(XmlConstants.AtomContentElementName, XmlConstants.AtomNamespace);
reader.ReadStartElement(XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace);
PlainXmlDeserializer.ApplyContent(this, reader, resourceType, resource, this.MaxObjectCount);
}
}
/// Reads the current object from the .
/// segmentinfo containing information about the current element that is getting processes
/// true if the element currently pointed by the xml reader refers to a top level element
/// Item to read from.
/// returns the clr object with the data populated
private object CreateObject(SegmentInfo segmentInfo, bool topLevel, SyndicationItem item)
{
Debug.Assert(item != null, "item != null");
Debug.Assert(topLevel || !this.Update, "deep updates not supported");
this.RecurseEnter();
object result;
// update the object count everytime you encounter a new resource
this.CheckAndIncrementObjectCount();
// Process the type annotation.
ResourceType currentResourceType = this.GetResourceType(item, segmentInfo.TargetElementType);
if (currentResourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_OnlyEntityTypesMustBeSpecifiedInEntryElement(currentResourceType.FullName));
}
// Get a resource cookie from the provider.
ResourceContainer container;
#if ASTORIA_OPEN_OBJECT
if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty)
{
container = this.Service.Provider.GetContainerForResourceType(currentResourceType.Type);
}
else
#endif
{
Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.Resource, "segmentInfo.TargetKind == RequestTargetKind.Resource");
container = segmentInfo.TargetContainer;
}
if (this.Update)
{
Debug.Assert(currentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "only expecting entity types");
bool verifyETag = topLevel && HasContent(item);
bool replaceResource = topLevel && this.Service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT;
// if its a top level resource, then it cannot be null
result = this.CreateObjectFromUri(currentResourceType, segmentInfo, (RequestDescription)null, verifyETag, topLevel /*checkForNull*/, replaceResource);
if (this.Tracker != null)
{
this.Tracker.TrackAction(result, container, UpdateOperations.Change);
}
}
else
{
if (segmentInfo.TargetKind == RequestTargetKind.Resource)
{
this.Service.Configuration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend);
}
result = this.Service.Provider.CreateResource(container.Name, currentResourceType.FullName);
if (this.Tracker != null)
{
this.Tracker.TrackAction(result, container, UpdateOperations.Add);
}
}
// Process the content in the entry.
if (item.Content != null)
{
string contentType = item.Content.Type;
if (contentType != XmlConstants.MimeApplicationXml)
{
throw DataServiceException.CreateBadRequestError(
Strings.Syndication_EntryContentTypeUnsupported(contentType));
}
XmlSyndicationContent itemContent = item.Content as XmlSyndicationContent;
Debug.Assert(itemContent != null, "itemContent != null -- otherwise formatter created the wrong type.");
this.ApplyContent(itemContent, currentResourceType, result);
}
// Process the links in the entry.
foreach (SyndicationLink link in item.Links)
{
string navigationPropertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(link.RelationshipType);
if (null == navigationPropertyName)
{
continue;
}
Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb);
this.ApplyLink(link, currentResourceType, result, navigationPropertyName);
}
this.RecurseLeave();
return result;
}
/// Applies the information from a link to the specified resource.
/// LinkDescriptor with information to apply.
/// Type for the target resource.
/// Target resource to which information will be applied.
/// Name of the property that this link represents.
private void ApplyLink(SyndicationLink link, ResourceType resourceType, object resource, string propertyName)
{
Debug.Assert(link != null, "link != null");
Debug.Assert(resourceType != null, "resourceType != null");
Debug.Assert(resource != null, "resource != null");
ResourceProperty property = resourceType.TryResolvePropertyName(propertyName);
if (
#if ASTORIA_OPEN_OBJECT
(property == null && resourceType.OpenTypeKind != OpenTypeKind.CompletelyOpen) ||
#else
property == null ||
#endif
(property != null && property.TypeKind != ResourceTypeKind.EntityType))
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidNavigationPropertyName(propertyName, resourceType.FullName));
}
string typeParameterValue = ValidateTypeParameterForNonOpenTypeProperties(link.MediaType, property);
LinkContent linkContent = this.HandleLinkContent(link, resource, property, typeParameterValue, propertyName);
#region Handle bind/unbind operation
// If the href was specified empty or an empty inline element was specified, then we will set the
// reference to null - this helps in overrriding if there was a default non-value for this property
// else if only link element was specified, and then href points to a single result, then we will
// perform a bind operation
if ((linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && String.IsNullOrEmpty(link.Uri.OriginalString)) ||
linkContent == LinkContent.EmptyInlineElementSpecified)
{
// update the object count when you are performing a bind operation
this.CheckAndIncrementObjectCount();
if (property != null && property.Kind == ResourcePropertyKind.ResourceSetReference)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(propertyName));
}
// For open properties, we will assume that this is a reference property and set it to null
this.Service.Provider.SetReference(resource, propertyName, null);
}
else if (linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && !String.IsNullOrEmpty(link.Uri.OriginalString))
{
// update the object count when you are performing a bind operation
this.CheckAndIncrementObjectCount();
// If the link points to a reference navigation property, then update the link
Uri referencedUri = RequestUriProcessor.GetAbsoluteUriFromReference(link.Uri.OriginalString, this.Service.RequestParams);
RequestDescription description = RequestUriProcessor.ProcessRequestUri(referencedUri, this.Service);
if (!description.IsSingleResult)
{
if (property != null && property.Kind == ResourcePropertyKind.ResourceReference)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_LinkHrefMustReferToSingleResource(propertyName));
}
return;
}
// no need to check for null. For collection properties, they can never be null and that
// check has been added below. For reference properties, if they are null, it means unbind
// and hence no need to check for null.
object propertyItem = CreateObjectFromUri(null, null, description, false /*verifyETag*/, false /*checkForNull*/, false /*replaceResource*/);
if (property != null)
{
if (property.Kind == ResourcePropertyKind.ResourceReference)
{
this.Service.Provider.SetReference(resource, propertyName, propertyItem);
}
else
{
WebUtil.CheckResourceExists(propertyItem != null, description.LastSegmentInfo.Identifier);
this.Service.Provider.AddReferenceToCollection(resource, propertyName, propertyItem);
}
}
else if (typeParameterValue == null)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MissingTypeParameterOnLinkElement(propertyName));
}
else if (typeParameterValue == XmlConstants.AtomEntryElementName)
{
this.Service.Provider.SetReference(resource, propertyName, propertyItem);
}
else
{
if (typeParameterValue != XmlConstants.AtomFeedElementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterInTypeAttributeInLink(propertyName, typeParameterValue));
}
WebUtil.CheckResourceExists(propertyItem != null, description.LastSegmentInfo.Identifier);
this.Service.Provider.AddReferenceToCollection(resource, propertyName, propertyItem);
}
}
#endregion Handle bind/unbind operation
}
/// Gets the type attribute and resolves the type.
/// Item from which type attribute needs to be read
/// Expected base type for the item.
/// Resolved type.
private ResourceType GetResourceType(SyndicationItem item, Type expectedType)
{
Debug.Assert(item != null, "item != null");
string typeName = SyndicationItemGetType(item);
ResourceType resourceType;
// If the type is not specified in the payload, we assume the type to be the expected type
if (String.IsNullOrEmpty(typeName))
{
// check if the expected type takes part in inheritance
resourceType = this.Service.Provider.GetResourceType(expectedType);
if (resourceType.HasDerivedTypes)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance);
}
}
else
{
// Otherwise, try and resolve the name specified in the payload
resourceType = this.Service.Provider.TryResolveTypeName(typeName);
if (resourceType == null)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(typeName));
}
}
return resourceType;
}
///
/// Handle the contents under the link element
///
/// syndication link element
/// parent resource which contains the link.
/// property representing the link.
/// type parameter value as specified in the type attribute.
/// name of the property that this link represents.
/// returns whether there are child elements under link element.
private LinkContent HandleLinkContent(
SyndicationLink link,
object parentResource,
ResourceProperty property,
string typeParameterValue,
string propertyName)
{
Debug.Assert(parentResource != null, "parent resource cannot be null");
Debug.Assert(link != null, "link != null");
LinkContent linkContent = LinkContent.NoInlineElementSpecified;
foreach (var e in link.ElementExtensions)
{
// link can contain other elements apart from the inline elements.
if (e.OuterNamespace != XmlConstants.DataWebMetadataNamespace ||
e.OuterName != XmlConstants.AtomInlineElementName)
{
continue;
}
// Deep payload cannot be specified for update
if (this.Update)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepUpdateNotSupported);
}
linkContent = LinkContent.EmptyInlineElementSpecified;
using (XmlReader linkReader = e.GetReader())
{
while (linkReader.Read())
{
if (linkReader.NodeType == XmlNodeType.Element)
{
string elementName = linkReader.LocalName;
string namespaceUri = linkReader.NamespaceURI;
if (namespaceUri != XmlConstants.AtomNamespace)
{
throw DataServiceException.CreateBadRequestError(
Strings.BadRequest_InlineElementMustContainValidElement(
elementName,
XmlConstants.AtomInlineElementName,
XmlConstants.AtomFeedElementName,
XmlConstants.AtomEntryElementName));
}
linkContent = LinkContent.InlineElementContainsData;
if (elementName == XmlConstants.AtomEntryElementName)
{
if (property != null)
{
if (property.Kind != ResourcePropertyKind.ResourceReference)
{
throw DataServiceException.CreateBadRequestError(Strings.Syndication_EntryElementForReferenceProperties(e.OuterName, propertyName));
}
}
else if (typeParameterValue != null && typeParameterValue != elementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomEntryElementName));
}
// Make sure if the media type is specified. If its specified, it should better be link
SyndicationItem propertyItem;
propertyItem = ReadSyndicationItem(this.factory.CreateSyndicationItemFormatter(), linkReader);
SegmentInfo propertySegment = CreateSegment(property, propertyName, true /* singleResult */);
object propertyValue = this.CreateObject(propertySegment, false /* topLevel */, propertyItem);
this.Service.Provider.SetReference(parentResource, propertyName, propertyValue);
}
else if (elementName == XmlConstants.AtomFeedElementName)
{
if (property != null)
{
if (property.Kind != ResourcePropertyKind.ResourceSetReference)
{
throw DataServiceException.CreateBadRequestError(Strings.Syndication_FeedElementForCollections(e.OuterName, propertyName));
}
}
else if (typeParameterValue != null && typeParameterValue != elementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomFeedElementName));
}
SyndicationFeed propertyFeed;
propertyFeed = ReadSyndicationFeed(this.factory.CreateSyndicationFeedFormatter(), linkReader);
SegmentInfo propertySegment = CreateSegment(property, propertyName, false /* singleResult */);
foreach (SyndicationItem item in propertyFeed.Items)
{
object propertyValue = this.CreateObject(propertySegment, false /* topLevel */, item);
if (propertyValue == null)
{
if (propertySegment.ProjectedProperty != null &&
propertySegment.ProjectedProperty.Kind == ResourcePropertyKind.ResourceSetReference)
{
throw DataServiceException.CreateBadRequestError(
Strings.BadRequest_CannotSetCollectionsToNull(propertyName));
}
}
Debug.Assert(
#if ASTORIA_OPEN_OBJECT
propertySegment.TargetKind == RequestTargetKind.OpenProperty ||
#endif
(propertySegment.TargetSource == RequestTargetSource.Property && propertySegment.TargetKind == RequestTargetKind.Resource && propertySegment.SingleResult == false),
"must be navigation set property or open property");
this.Service.Provider.AddReferenceToCollection(parentResource, propertyName, propertyValue);
}
}
else
{
throw DataServiceException.CreateBadRequestError(
Strings.BadRequest_InlineElementMustContainValidElement(
elementName,
XmlConstants.AtomInlineElementName,
XmlConstants.AtomFeedElementName,
XmlConstants.AtomEntryElementName));
}
}
}
}
}
return linkContent;
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Provides a deserializer for atom content structured as
// syndication items or feeds.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Serializers
{
#region Namespaces.
using System;
using System.Collections.Generic;
using System.Data.Services.Providers;
using System.Diagnostics;
using System.Linq;
using System.IO;
using System.ServiceModel.Syndication;
using System.Text;
using System.Xml;
#endregion Namespaces.
/// Provides a deserializer for structured content.
internal class SyndicationDeserializer : Deserializer
{
/// Factory for syndication formatting objects.
private readonly SyndicationFormatterFactory factory;
/// reader to read xml from the request stream
private readonly XmlReader xmlReader;
/// Initializes a new for the specified stream.
/// Input stream reader from which ATOM content must be read.
/// Encoding to use for the stream (null to auto-discover).
/// Data service for which the deserializer will act.
/// indicates whether this is a update operation or not
/// Factory for formatter objects.
/// Tracker to use for modifications.
internal SyndicationDeserializer(Stream stream, Encoding encoding, IDataService dataService, bool update, SyndicationFormatterFactory factory, UpdateTracker tracker)
: base(update, dataService, tracker)
{
Debug.Assert(stream != null, "stream != null");
Debug.Assert(factory != null, "factory != null");
this.factory = factory;
this.xmlReader = factory.CreateReader(stream, encoding);
}
///
/// Indicates the various form of data in the inline xml element
///
private enum LinkContent
{
/// If the link element didn't not contain an inline element at all.
NoInlineElementSpecified,
/// If the link element contained an empty inline element.
EmptyInlineElementSpecified,
/// If the inline element under the link element contained some data.
InlineElementContainsData
}
/// returns the content format for the deserializer
protected override ContentFormat ContentFormat
{
get
{
return ContentFormat.Atom;
}
}
///
/// Assumes the payload to represent a single object and processes accordingly
///
/// info about the object being created
/// the newly formed object that the payload represents
protected override object CreateSingleObject(SegmentInfo segmentInfo)
{
SyndicationItem item = ReadSyndicationItem(this.factory.CreateSyndicationItemFormatter(), this.xmlReader);
return this.CreateObject(segmentInfo, true /*topLevel*/, item);
}
/// Provides an opportunity to clean-up resources.
///
/// Whether the call is being made from an explicit call to
/// IDisposable.Dispose() rather than through the finalizer.
///
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
this.xmlReader.Close();
}
}
///
/// Get the resource referred by the uri in the payload
///
/// resource referred by the uri in the payload.
protected override string GetLinkUriFromPayload()
{
throw Error.NotImplemented();
}
/// Checks whether the specified item has a payload.
/// Item to check.
/// true if the item has content or links specified; false otherwise.
private static bool HasContent(SyndicationItem item)
{
return item.Content != null || item.Links.Count > 0;
}
/// Gets the text for the type annotated on the specified .
/// Item to read type from.
/// The text for the type annotated on the specified item; null if none is set.
private static string SyndicationItemGetType(SyndicationItem item)
{
Debug.Assert(item != null, "item != null");
SyndicationCategory category = item.Categories.Where(c => c.Scheme == XmlConstants.DataWebSchemeNamespace).FirstOrDefault();
return (category == null) ? null : category.Name;
}
/// Reads a SyndicationFeed object from the specified XmlReader.
/// Formatter to use when reading content.
/// Read to read feed from.
/// A new SyndicationFeed instance.
private static SyndicationFeed ReadSyndicationFeed(SyndicationFeedFormatter formatter, XmlReader reader)
{
Debug.Assert(formatter != null, "formatter != null");
Debug.Assert(reader != null, "reader != null");
try
{
formatter.ReadFrom(reader);
}
catch (XmlException exception)
{
throw DataServiceException.CreateBadRequestError(Strings.Syndication_ErrorReadingFeed(exception.Message), exception);
}
Debug.Assert(formatter.Feed != null, "formatter.Feed != null");
return formatter.Feed;
}
/// Reads a SyndicationItem object from the specified XmlReader.
/// Formatter to use when reading content.
/// Read to read feed from.
/// A new SyndicationItem instance.
private static SyndicationItem ReadSyndicationItem(SyndicationItemFormatter formatter, XmlReader reader)
{
Debug.Assert(formatter != null, "formatter != null");
Debug.Assert(reader != null, "reader != null");
try
{
formatter.ReadFrom(reader);
}
catch (XmlException exception)
{
throw DataServiceException.CreateBadRequestError(Strings.Syndication_ErrorReadingEntry(exception.Message), exception);
}
Debug.Assert(formatter.Item != null, "formatter.Item != null");
return formatter.Item;
}
///
/// Read the link media type and validate for non open property types
///
/// media type as specified on the link element.
/// property which the link represents.
/// returns the type parameters specified in the media link.
private static string ValidateTypeParameterForNonOpenTypeProperties(string mediaType, ResourceProperty property)
{
string typeParameterValue = null;
if (!String.IsNullOrEmpty(mediaType))
{
string mime;
Encoding encoding;
KeyValuePair[] contentTypeParameters = HttpProcessUtility.ReadContentType(mediaType, out mime, out encoding);
if (mime != XmlConstants.MimeApplicationAtom)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MimeTypeMustBeApplicationAtom(mime, XmlConstants.MimeApplicationAtom));
}
// If the type parameter is specified, make sure its correct. We do the validation for known properties here
// and for open-properties, the validation is done if the link is expanded. Otherwise, there is no good way of
// doing the validation.
typeParameterValue = HttpProcessUtility.GetParameterValue(contentTypeParameters, XmlConstants.AtomTypeAttributeName);
if (!String.IsNullOrEmpty(typeParameterValue) && property != null)
{
if (property.Kind == ResourcePropertyKind.ResourceReference)
{
if (typeParameterValue != XmlConstants.AtomEntryElementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomEntryElementName));
}
}
else if (typeParameterValue != XmlConstants.AtomFeedElementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomFeedElementName));
}
}
}
return typeParameterValue;
}
/// Applies the properties in the plain XML content to the specified resource.
/// Content to read values from.
/// Type of resource whose values are being set.
/// Target resource.
private void ApplyContent(XmlSyndicationContent content, ResourceType resourceType, object resource)
{
Debug.Assert(content != null, "content != null");
using (XmlReader reader = content.GetReaderAtContent())
{
WebUtil.XmlReaderEnsureElement(reader);
Debug.Assert(
reader.NodeType == XmlNodeType.Element,
reader.NodeType.ToString() + " == XmlNodeType.Element -- otherwise XmlSyndicationContent didn't see a 'content' tag");
reader.ReadStartElement(XmlConstants.AtomContentElementName, XmlConstants.AtomNamespace);
reader.ReadStartElement(XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace);
PlainXmlDeserializer.ApplyContent(this, reader, resourceType, resource, this.MaxObjectCount);
}
}
/// Reads the current object from the .
/// segmentinfo containing information about the current element that is getting processes
/// true if the element currently pointed by the xml reader refers to a top level element
/// Item to read from.
/// returns the clr object with the data populated
private object CreateObject(SegmentInfo segmentInfo, bool topLevel, SyndicationItem item)
{
Debug.Assert(item != null, "item != null");
Debug.Assert(topLevel || !this.Update, "deep updates not supported");
this.RecurseEnter();
object result;
// update the object count everytime you encounter a new resource
this.CheckAndIncrementObjectCount();
// Process the type annotation.
ResourceType currentResourceType = this.GetResourceType(item, segmentInfo.TargetElementType);
if (currentResourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_OnlyEntityTypesMustBeSpecifiedInEntryElement(currentResourceType.FullName));
}
// Get a resource cookie from the provider.
ResourceContainer container;
#if ASTORIA_OPEN_OBJECT
if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty)
{
container = this.Service.Provider.GetContainerForResourceType(currentResourceType.Type);
}
else
#endif
{
Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.Resource, "segmentInfo.TargetKind == RequestTargetKind.Resource");
container = segmentInfo.TargetContainer;
}
if (this.Update)
{
Debug.Assert(currentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "only expecting entity types");
bool verifyETag = topLevel && HasContent(item);
bool replaceResource = topLevel && this.Service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT;
// if its a top level resource, then it cannot be null
result = this.CreateObjectFromUri(currentResourceType, segmentInfo, (RequestDescription)null, verifyETag, topLevel /*checkForNull*/, replaceResource);
if (this.Tracker != null)
{
this.Tracker.TrackAction(result, container, UpdateOperations.Change);
}
}
else
{
if (segmentInfo.TargetKind == RequestTargetKind.Resource)
{
this.Service.Configuration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend);
}
result = this.Service.Provider.CreateResource(container.Name, currentResourceType.FullName);
if (this.Tracker != null)
{
this.Tracker.TrackAction(result, container, UpdateOperations.Add);
}
}
// Process the content in the entry.
if (item.Content != null)
{
string contentType = item.Content.Type;
if (contentType != XmlConstants.MimeApplicationXml)
{
throw DataServiceException.CreateBadRequestError(
Strings.Syndication_EntryContentTypeUnsupported(contentType));
}
XmlSyndicationContent itemContent = item.Content as XmlSyndicationContent;
Debug.Assert(itemContent != null, "itemContent != null -- otherwise formatter created the wrong type.");
this.ApplyContent(itemContent, currentResourceType, result);
}
// Process the links in the entry.
foreach (SyndicationLink link in item.Links)
{
string navigationPropertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(link.RelationshipType);
if (null == navigationPropertyName)
{
continue;
}
Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb);
this.ApplyLink(link, currentResourceType, result, navigationPropertyName);
}
this.RecurseLeave();
return result;
}
/// Applies the information from a link to the specified resource.
/// LinkDescriptor with information to apply.
/// Type for the target resource.
/// Target resource to which information will be applied.
/// Name of the property that this link represents.
private void ApplyLink(SyndicationLink link, ResourceType resourceType, object resource, string propertyName)
{
Debug.Assert(link != null, "link != null");
Debug.Assert(resourceType != null, "resourceType != null");
Debug.Assert(resource != null, "resource != null");
ResourceProperty property = resourceType.TryResolvePropertyName(propertyName);
if (
#if ASTORIA_OPEN_OBJECT
(property == null && resourceType.OpenTypeKind != OpenTypeKind.CompletelyOpen) ||
#else
property == null ||
#endif
(property != null && property.TypeKind != ResourceTypeKind.EntityType))
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidNavigationPropertyName(propertyName, resourceType.FullName));
}
string typeParameterValue = ValidateTypeParameterForNonOpenTypeProperties(link.MediaType, property);
LinkContent linkContent = this.HandleLinkContent(link, resource, property, typeParameterValue, propertyName);
#region Handle bind/unbind operation
// If the href was specified empty or an empty inline element was specified, then we will set the
// reference to null - this helps in overrriding if there was a default non-value for this property
// else if only link element was specified, and then href points to a single result, then we will
// perform a bind operation
if ((linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && String.IsNullOrEmpty(link.Uri.OriginalString)) ||
linkContent == LinkContent.EmptyInlineElementSpecified)
{
// update the object count when you are performing a bind operation
this.CheckAndIncrementObjectCount();
if (property != null && property.Kind == ResourcePropertyKind.ResourceSetReference)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(propertyName));
}
// For open properties, we will assume that this is a reference property and set it to null
this.Service.Provider.SetReference(resource, propertyName, null);
}
else if (linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && !String.IsNullOrEmpty(link.Uri.OriginalString))
{
// update the object count when you are performing a bind operation
this.CheckAndIncrementObjectCount();
// If the link points to a reference navigation property, then update the link
Uri referencedUri = RequestUriProcessor.GetAbsoluteUriFromReference(link.Uri.OriginalString, this.Service.RequestParams);
RequestDescription description = RequestUriProcessor.ProcessRequestUri(referencedUri, this.Service);
if (!description.IsSingleResult)
{
if (property != null && property.Kind == ResourcePropertyKind.ResourceReference)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_LinkHrefMustReferToSingleResource(propertyName));
}
return;
}
// no need to check for null. For collection properties, they can never be null and that
// check has been added below. For reference properties, if they are null, it means unbind
// and hence no need to check for null.
object propertyItem = CreateObjectFromUri(null, null, description, false /*verifyETag*/, false /*checkForNull*/, false /*replaceResource*/);
if (property != null)
{
if (property.Kind == ResourcePropertyKind.ResourceReference)
{
this.Service.Provider.SetReference(resource, propertyName, propertyItem);
}
else
{
WebUtil.CheckResourceExists(propertyItem != null, description.LastSegmentInfo.Identifier);
this.Service.Provider.AddReferenceToCollection(resource, propertyName, propertyItem);
}
}
else if (typeParameterValue == null)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MissingTypeParameterOnLinkElement(propertyName));
}
else if (typeParameterValue == XmlConstants.AtomEntryElementName)
{
this.Service.Provider.SetReference(resource, propertyName, propertyItem);
}
else
{
if (typeParameterValue != XmlConstants.AtomFeedElementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterInTypeAttributeInLink(propertyName, typeParameterValue));
}
WebUtil.CheckResourceExists(propertyItem != null, description.LastSegmentInfo.Identifier);
this.Service.Provider.AddReferenceToCollection(resource, propertyName, propertyItem);
}
}
#endregion Handle bind/unbind operation
}
/// Gets the type attribute and resolves the type.
/// Item from which type attribute needs to be read
/// Expected base type for the item.
/// Resolved type.
private ResourceType GetResourceType(SyndicationItem item, Type expectedType)
{
Debug.Assert(item != null, "item != null");
string typeName = SyndicationItemGetType(item);
ResourceType resourceType;
// If the type is not specified in the payload, we assume the type to be the expected type
if (String.IsNullOrEmpty(typeName))
{
// check if the expected type takes part in inheritance
resourceType = this.Service.Provider.GetResourceType(expectedType);
if (resourceType.HasDerivedTypes)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance);
}
}
else
{
// Otherwise, try and resolve the name specified in the payload
resourceType = this.Service.Provider.TryResolveTypeName(typeName);
if (resourceType == null)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(typeName));
}
}
return resourceType;
}
///
/// Handle the contents under the link element
///
/// syndication link element
/// parent resource which contains the link.
/// property representing the link.
/// type parameter value as specified in the type attribute.
/// name of the property that this link represents.
/// returns whether there are child elements under link element.
private LinkContent HandleLinkContent(
SyndicationLink link,
object parentResource,
ResourceProperty property,
string typeParameterValue,
string propertyName)
{
Debug.Assert(parentResource != null, "parent resource cannot be null");
Debug.Assert(link != null, "link != null");
LinkContent linkContent = LinkContent.NoInlineElementSpecified;
foreach (var e in link.ElementExtensions)
{
// link can contain other elements apart from the inline elements.
if (e.OuterNamespace != XmlConstants.DataWebMetadataNamespace ||
e.OuterName != XmlConstants.AtomInlineElementName)
{
continue;
}
// Deep payload cannot be specified for update
if (this.Update)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepUpdateNotSupported);
}
linkContent = LinkContent.EmptyInlineElementSpecified;
using (XmlReader linkReader = e.GetReader())
{
while (linkReader.Read())
{
if (linkReader.NodeType == XmlNodeType.Element)
{
string elementName = linkReader.LocalName;
string namespaceUri = linkReader.NamespaceURI;
if (namespaceUri != XmlConstants.AtomNamespace)
{
throw DataServiceException.CreateBadRequestError(
Strings.BadRequest_InlineElementMustContainValidElement(
elementName,
XmlConstants.AtomInlineElementName,
XmlConstants.AtomFeedElementName,
XmlConstants.AtomEntryElementName));
}
linkContent = LinkContent.InlineElementContainsData;
if (elementName == XmlConstants.AtomEntryElementName)
{
if (property != null)
{
if (property.Kind != ResourcePropertyKind.ResourceReference)
{
throw DataServiceException.CreateBadRequestError(Strings.Syndication_EntryElementForReferenceProperties(e.OuterName, propertyName));
}
}
else if (typeParameterValue != null && typeParameterValue != elementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomEntryElementName));
}
// Make sure if the media type is specified. If its specified, it should better be link
SyndicationItem propertyItem;
propertyItem = ReadSyndicationItem(this.factory.CreateSyndicationItemFormatter(), linkReader);
SegmentInfo propertySegment = CreateSegment(property, propertyName, true /* singleResult */);
object propertyValue = this.CreateObject(propertySegment, false /* topLevel */, propertyItem);
this.Service.Provider.SetReference(parentResource, propertyName, propertyValue);
}
else if (elementName == XmlConstants.AtomFeedElementName)
{
if (property != null)
{
if (property.Kind != ResourcePropertyKind.ResourceSetReference)
{
throw DataServiceException.CreateBadRequestError(Strings.Syndication_FeedElementForCollections(e.OuterName, propertyName));
}
}
else if (typeParameterValue != null && typeParameterValue != elementName)
{
throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomFeedElementName));
}
SyndicationFeed propertyFeed;
propertyFeed = ReadSyndicationFeed(this.factory.CreateSyndicationFeedFormatter(), linkReader);
SegmentInfo propertySegment = CreateSegment(property, propertyName, false /* singleResult */);
foreach (SyndicationItem item in propertyFeed.Items)
{
object propertyValue = this.CreateObject(propertySegment, false /* topLevel */, item);
if (propertyValue == null)
{
if (propertySegment.ProjectedProperty != null &&
propertySegment.ProjectedProperty.Kind == ResourcePropertyKind.ResourceSetReference)
{
throw DataServiceException.CreateBadRequestError(
Strings.BadRequest_CannotSetCollectionsToNull(propertyName));
}
}
Debug.Assert(
#if ASTORIA_OPEN_OBJECT
propertySegment.TargetKind == RequestTargetKind.OpenProperty ||
#endif
(propertySegment.TargetSource == RequestTargetSource.Property && propertySegment.TargetKind == RequestTargetKind.Resource && propertySegment.SingleResult == false),
"must be navigation set property or open property");
this.Service.Provider.AddReferenceToCollection(parentResource, propertyName, propertyValue);
}
}
else
{
throw DataServiceException.CreateBadRequestError(
Strings.BadRequest_InlineElementMustContainValidElement(
elementName,
XmlConstants.AtomInlineElementName,
XmlConstants.AtomFeedElementName,
XmlConstants.AtomEntryElementName));
}
}
}
}
}
return linkContent;
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- FixedDocumentSequencePaginator.cs
- FileRecordSequenceHelper.cs
- OnOperation.cs
- BindingWorker.cs
- PriorityItem.cs
- TextEditorThreadLocalStore.cs
- KeyMatchBuilder.cs
- LineInfo.cs
- CollectionBuilder.cs
- ToolboxComponentsCreatedEventArgs.cs
- AutomationPeer.cs
- FastPropertyAccessor.cs
- PropertyInfo.cs
- ComponentCommands.cs
- DiscoveryClientDocuments.cs
- OutputCacheSettings.cs
- UrlAuthFailedErrorFormatter.cs
- WebBrowserNavigatingEventHandler.cs
- ObjectDataSourceMethodEventArgs.cs
- ReliabilityContractAttribute.cs
- FontEmbeddingManager.cs
- SID.cs
- Filter.cs
- EdgeProfileValidation.cs
- Header.cs
- SQLDecimal.cs
- AddingNewEventArgs.cs
- NameValuePermission.cs
- DateTimeConverter.cs
- ResourceProviderFactory.cs
- HttpCookieCollection.cs
- OleCmdHelper.cs
- SynchronizedDispatch.cs
- LoadMessageLogger.cs
- SchemaMapping.cs
- SystemResources.cs
- Socket.cs
- X509Extension.cs
- PipelineModuleStepContainer.cs
- PermissionSet.cs
- KnownTypeAttribute.cs
- Reference.cs
- HtmlInputPassword.cs
- UITypeEditor.cs
- TabControl.cs
- EntityWrapper.cs
- DesignerVerbToolStripMenuItem.cs
- MouseActionConverter.cs
- ColorAnimationBase.cs
- Viewport3DVisual.cs
- WebPartPersonalization.cs
- FileIOPermission.cs
- TabletCollection.cs
- SoapSchemaMember.cs
- KoreanLunisolarCalendar.cs
- QueueAccessMode.cs
- DependencyObjectCodeDomSerializer.cs
- EntryIndex.cs
- ViewStateModeByIdAttribute.cs
- AnnotationObservableCollection.cs
- DataGridViewSelectedCellsAccessibleObject.cs
- IResourceProvider.cs
- XmlILOptimizerVisitor.cs
- MatrixAnimationUsingKeyFrames.cs
- TextBlockAutomationPeer.cs
- NativeMethodsOther.cs
- SBCSCodePageEncoding.cs
- BooleanExpr.cs
- StatusBar.cs
- EntityTypeEmitter.cs
- XPathChildIterator.cs
- AlignmentYValidation.cs
- WebEventCodes.cs
- CodeObjectCreateExpression.cs
- Size.cs
- BufferedReadStream.cs
- StringDictionaryWithComparer.cs
- AssemblyResourceLoader.cs
- formatter.cs
- WebPartVerb.cs
- TextTreeInsertUndoUnit.cs
- WorkflowMessageEventArgs.cs
- parserscommon.cs
- DbReferenceCollection.cs
- TreeNode.cs
- OleDbFactory.cs
- SoapAttributeOverrides.cs
- CodeFieldReferenceExpression.cs
- InheritanceContextHelper.cs
- VSDExceptions.cs
- X509ChainPolicy.cs
- MemoryMappedViewStream.cs
- SmiEventSink_Default.cs
- SchemaImporterExtensionElementCollection.cs
- CodeMemberEvent.cs
- ImageIndexConverter.cs
- GZipObjectSerializer.cs
- AvTrace.cs
- CodeTypeDeclaration.cs
- WebConfigurationHostFileChange.cs