Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / Serializer.cs / 3 / Serializer.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// An abstract base serializer for various serializers
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Serializers
{
#region Namespaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services.Providers;
using System.Diagnostics;
using System.IO;
using System.Text;
#endregion Namespaces.
/// Abstract base class for all serializers.
internal abstract class Serializer : IExceptionWriter
{
#region Private fields.
/// Base URI from which resources should be resolved.
private readonly Uri absoluteServiceUri;
/// Configuration for the service.
private readonly DataServiceConfiguration configuration;
/// Data provider from which metadata should be gathered.
private readonly string httpETagHeaderValue;
/// Data provider from which metadata should be gathered.
private readonly IDataServiceProvider provider;
/// Description for the requested results.
private readonly RequestDescription requestDescription;
/// Collection of complex types, used for cycle detection.
private HashSet complexTypeCollection;
/// Resolved segment containers.
private List segmentContainers;
/// Segment names.
private List segmentNames;
/// Result counts for segments.
private List segmentResultCounts;
#endregion Private fields.
/// Initializes a new base Serializer, ready to write out a description.
/// Description for the requested results.
/// Base URI from which resources should be resolved.
/// Service with configuration and provider from which metadata should be gathered.
/// HTTP ETag header value.
internal Serializer(RequestDescription requestDescription, Uri absoluteServiceUri, IDataService service, string httpETagHeaderValue)
{
Debug.Assert(requestDescription != null, "requestDescription != null");
Debug.Assert(absoluteServiceUri != null, "absoluteServiceUri != null");
Debug.Assert(service != null, "service != null");
this.requestDescription = requestDescription;
this.absoluteServiceUri = absoluteServiceUri;
this.configuration = service.Configuration;
this.provider = service.Provider;
this.httpETagHeaderValue = httpETagHeaderValue;
}
/// Container for the resource being serialized (possibly null).
protected ResourceContainer CurrentContainer
{
get
{
if (this.segmentContainers == null || this.segmentContainers.Count == 0)
{
return this.requestDescription.LastSegmentInfo.TargetContainer;
}
else
{
return this.segmentContainers[this.segmentContainers.Count - 1];
}
}
}
///
/// Gets the Data provider from which metadata should be gathered.
///
protected IDataServiceProvider Provider
{
[DebuggerStepThrough]
get { return this.provider; }
}
/// Gets the absolute URI to the service.
protected Uri AbsoluteServiceUri
{
[DebuggerStepThrough]
get { return this.absoluteServiceUri; }
}
///
/// Gets the RequestDescription for the request that is getting serialized.
///
protected RequestDescription RequestDescription
{
[DebuggerStepThrough]
get
{
return this.requestDescription;
}
}
/// Serializes exception information.
/// Description of exception to serialize.
public abstract void WriteException(HandleExceptionArgs args);
///
/// Gets the uri given the list of resource properties. This logic must be the same across all
/// serializers. Hence putting this in a util class
///
/// instance of the resource type whose properties needs to be returned
/// Provider from which resource was obtained.
/// Container for the resource.
/// Base URI from which resources should be resolved.
/// uri for the given resource
internal static Uri GetUri(object resource, IDataServiceProvider provider, ResourceContainer container, Uri absoluteServiceUri)
{
string objectKey = GetObjectKey(resource, provider, container.Name);
return RequestUriProcessor.AppendEscapedSegment(absoluteServiceUri, objectKey);
}
///
/// Appends the given entry to the given uri
///
/// uri to which the entry needs to be appended
/// entry which gets appended to the given uri
/// new uri with the entry appended to the given uri
internal static Uri AppendEntryToUri(Uri currentUri, string entry)
{
return RequestUriProcessor.AppendUnescapedSegment(currentUri, entry);
}
///
/// Handles the complete serialization for the specified .
///
/// Query results to enumerate.
/// Whether was succesfully advanced to the first element.
///
/// should correspond to the RequestQuery of the
/// RequestDescription object passed while constructing this serializer
/// We allow the results to be passed in
/// to let the query be executed earlier than at result-writing time, which
/// helps detect data and query errors where they can be better handled.
///
internal void WriteRequest(IEnumerator queryResults, bool hasMoved)
{
IEnumerable requestEnumerable = this.requestDescription.RequestEnumerable;
Debug.Assert(requestEnumerable != null, "requestQuery != null");
Debug.Assert(queryResults != null, "queryResults != null");
IExpandedResult expanded = queryResults as IExpandedResult;
if (this.requestDescription.LinkUri)
{
this.PushSegmentForRoot();
if (this.requestDescription.IsSingleResult)
{
this.WriteLink(queryResults.Current);
if (queryResults.MoveNext())
{
throw new InvalidOperationException(Strings.SingleResourceExpected);
}
}
else
{
this.WriteLinkCollection(queryResults, hasMoved);
}
this.PopSegmentName();
}
else if (this.requestDescription.IsSingleResult)
{
Debug.Assert(hasMoved == true, "hasMoved == true");
this.WriteTopLevelElement(expanded, queryResults.Current);
if (queryResults.MoveNext())
{
throw new InvalidOperationException(Strings.SingleResourceExpected);
}
}
else
{
this.WriteTopLevelElements(expanded, queryResults, hasMoved);
}
this.Flush();
}
/// Gets the expandable value for the specified object.
/// Expanded properties for the result, possibly null.
/// Object with value to retrieve.
/// Property for which value will be retrieved.
/// The property value.
protected static object GetExpandedProperty(IExpandedResult expanded, object customObject, ResourceProperty property)
{
Debug.Assert(property != null, "property != null");
if (expanded == null)
{
return property.GetValue(customObject);
}
else
{
return expanded.GetExpandedPropertyValue(property.Name);
}
}
/// Flushes the writer to the underlying stream.
protected abstract void Flush();
/// Writes a single top-level element.
/// Expanded properties for the result.
/// Element to write, possibly null.
protected abstract void WriteTopLevelElement(IExpandedResult expanded, object element);
/// Writes multiple top-level elements, possibly none.
/// Expanded properties for the result.
/// Result elements.
/// Whether was succesfully advanced to the first element.
protected abstract void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved);
///
/// Write out the uri for the given element
///
/// element whose uri needs to be written out.
protected abstract void WriteLink(object element);
///
/// Write out the uri for the given elements
///
/// elements whose uri need to be writtne out
/// the current state of the enumerator.
protected abstract void WriteLinkCollection(IEnumerator elements, bool hasMoved);
///
/// Adds the given object instance to complex type collection
///
/// instance to be added
/// true, if it got added successfully
protected bool AddToComplexTypeCollection(object complexTypeInstance)
{
if (this.complexTypeCollection == null)
{
this.complexTypeCollection = new HashSet(ObjectReferenceEqualityComparer.Instance);
}
return this.complexTypeCollection.Add(complexTypeInstance);
}
/// Gets the for the object.
/// Object being written.
/// Resource container for current object.
/// This method relies on segments being pushed and popped for MEST scenarios.
protected ResourceContainer GetContainerForCurrent(object current)
{
Debug.Assert(current != null, "current != null");
ResourceContainer result = this.CurrentContainer;
if (result == null)
{
result = this.Provider.GetContainerForResourceType(current.GetType());
}
return result;
}
#if ASTORIA_OPEN_OBJECT
/// Pushes a segment for the root of the tree being written out.
/// Name of open property.
/// Resulved type of open property.
/// Calls to this method should be balanced with calls to PopSegmentName.
protected void PushSegmentForOpenProperty(string propertyName, ResourceType propertyResourceType)
{
Debug.Assert(propertyName != null, "propertyName != null");
Debug.Assert(propertyResourceType != null, "propertyResourceType != null");
ResourceContainer container = this.provider.GetContainerForResourceType(propertyResourceType.Type);
this.PushSegment(propertyName, container);
}
#endif
/// Increments the result count for the current segment, throws if exceeds the limit.
protected void IncrementSegmentResultCount()
{
if (this.segmentResultCounts != null)
{
Debug.Assert(this.segmentResultCounts.Count > 0, "this.segmentResultCounts.Count > 0 -- otherwise we didn't PushSegmentForRoot");
int max = this.configuration.MaxResultsPerCollection;
if (max != Int32.MaxValue)
{
int count = this.segmentResultCounts[this.segmentResultCounts.Count - 1];
checked
{
count++;
}
if (count > max)
{
throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError);
}
this.segmentResultCounts[this.segmentResultCounts.Count - 1] = count;
}
}
}
/// Pushes a segment from the stack of names being written.
/// Property to push.
/// Calls to this method should be balanced with calls to PopSegmentName.
protected void PushSegmentForProperty(ResourceProperty property)
{
Debug.Assert(property != null, "property != null");
ResourceContainer current = null;
if ((property.Kind & (ResourcePropertyKind.ResourceReference | ResourcePropertyKind.ResourceSetReference)) != 0)
{
current = this.CurrentContainer;
if (current != null)
{
current = this.provider.GetContainer(current.Name, current.ResourceType.Type, property);
}
}
this.PushSegment(property.Name, current);
}
/// Pushes a segment for the root of the tree being written out.
/// Calls to this method should be balanced with calls to PopSegmentName.
protected void PushSegmentForRoot()
{
this.PushSegment(this.RequestDescription.ContainerName, this.CurrentContainer);
}
/// Pops a segment name from the stack of names being written.
/// Calls to this method should be balanced with previous calls to PushSegmentName.
protected void PopSegmentName()
{
if (this.segmentNames != null)
{
Debug.Assert(this.segmentNames.Count > 0, "this.segmentNames.Count > 0");
this.segmentNames.RemoveAt(this.segmentNames.Count - 1);
this.segmentContainers.RemoveAt(this.segmentContainers.Count - 1);
this.segmentResultCounts.RemoveAt(this.segmentResultCounts.Count - 1);
Debug.Assert(
this.segmentContainers.Count == this.segmentNames.Count,
"this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one");
Debug.Assert(
this.segmentContainers.Count == this.segmentResultCounts.Count,
"this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one");
}
}
/// Returns a clone of the segment names and containers trackd at this moment.
/// A clone of the segment names and containers tracked at this moment; possibly null.
protected object SaveSegmentNames()
{
if (this.segmentNames == null)
{
return null;
}
else
{
return new object[3]
{
new List(this.segmentNames),
new List(this.segmentContainers),
new List(this.segmentResultCounts)
};
}
}
/// Restores the segment names saved through .
/// Value returned from a previous call to .
protected void RestoreSegmentNames(object savedSegmentNames)
{
object[] savedLists = (object[])savedSegmentNames;
if (savedLists == null)
{
this.segmentNames = null;
this.segmentContainers = null;
this.segmentResultCounts = null;
}
else
{
this.segmentNames = (List)savedLists[0];
this.segmentContainers = (List)savedLists[1];
this.segmentResultCounts = (List)savedLists[2];
}
}
///
/// Remove the given object instance from the complex type collection
///
/// instance to be removed
protected void RemoveFromComplexTypeCollection(object complexTypeInstance)
{
Debug.Assert(this.complexTypeCollection != null, "this.complexTypeCollection != null");
Debug.Assert(this.complexTypeCollection.Contains(complexTypeInstance), "this.complexTypeCollection.Contains(complexTypeInstance)");
this.complexTypeCollection.Remove(complexTypeInstance);
}
/// Checks whether the property with the specified name should be expanded in-line.
/// Name of property to consider for expansion.
/// true if the segment should be expanded; false otherwise.
protected bool ShouldExpandSegment(string name)
{
Debug.Assert(name != null, "name != null");
if (this.segmentNames == null)
{
return false;
}
for (int i = 0; i < this.requestDescription.ExpandPaths.Count; i++)
{
List expandPath = this.requestDescription.ExpandPaths[i];
if (expandPath.Count < this.segmentNames.Count)
{
continue;
}
// We start off at '1' for segment names because the first one is the
// "this" in the query (/Customers?$expand=Orders doesn't include "Customers").
bool matchFound = true;
for (int j = 1; j < this.segmentNames.Count; j++)
{
if (expandPath[j - 1].Name != this.segmentNames[j])
{
matchFound = false;
break;
}
}
if (matchFound && expandPath[this.segmentNames.Count - 1].Name == name)
{
return true;
}
}
return false;
}
///
/// Returns the ETag value from the host response header
///
/// resource whose etag value gets to be returned
/// returns the etag value for the given resource
protected string GetETagValue(object resource)
{
if (this.requestDescription.IsSingleResult)
{
return this.httpETagHeaderValue;
}
else
{
return WebUtil.GetETagValue(this.provider, resource, this.GetContainerForCurrent(resource));
}
}
/// Returns the key for the given resource.
/// Resource for which key value needs to be returned.
/// Specific provider from which resource was obtained.
/// name of the entity container that the resource belongs to
/// Key for the given resource, with values encoded for use in a URI.
private static string GetObjectKey(object resource, IDataServiceProvider provider, string containerName)
{
Debug.Assert(resource != null, "resource != null");
Debug.Assert(provider != null, "provider != null");
Debug.Assert(!String.IsNullOrEmpty(containerName), "container name must be specified");
StringBuilder resultBuilder = new StringBuilder();
resultBuilder.Append(containerName);
resultBuilder.Append('(');
ResourceType resourceType = provider.GetResourceType(resource.GetType());
Debug.Assert(resourceType != null, "resourceType != null");
List keyProperties = resourceType.KeyProperties;
Debug.Assert(keyProperties.Count != 0, "every resource type must have a key");
for (int i = 0; i < keyProperties.Count; i++)
{
ResourceProperty property = keyProperties[i];
Debug.Assert(property.IsOfKind(ResourcePropertyKind.Key), "must be key property");
object keyValue = property.GetValue(resource);
if (keyValue == null)
{
// null keys not supported.
throw new InvalidOperationException(Strings.Serializer_NullKeysAreNotSupported(property.Name));
}
if (i == 0)
{
if (keyProperties.Count != 1)
{
resultBuilder.Append(property.Name);
resultBuilder.Append('=');
}
}
else
{
resultBuilder.Append(',');
resultBuilder.Append(property.Name);
resultBuilder.Append('=');
}
string keyValueText;
if (!System.Data.Services.Parsing.WebConvert.TryKeyPrimitiveToString(keyValue, out keyValueText))
{
throw new InvalidOperationException(Strings.Serializer_CannotConvertValue(keyValue));
}
Debug.Assert(keyValueText != null, "keyValueText != null - otherwise TryKeyPrimitiveToString returned true and null value");
resultBuilder.Append(System.Uri.EscapeDataString(keyValueText));
}
resultBuilder.Append(')');
return resultBuilder.ToString();
}
/// Pushes a segment from the stack of names being written.
/// Name of property to push.
/// Container to push (possibly null).
/// Calls to this method should be balanced with calls to PopSegmentName.
private void PushSegment(string name, ResourceContainer container)
{
if (this.configuration.MaxResultsPerCollection != Int32.MaxValue ||
(this.requestDescription.ExpandPaths != null &&
this.requestDescription.ExpandPaths.Count > 0))
{
if (this.segmentNames == null)
{
this.segmentNames = new List();
this.segmentContainers = new List();
this.segmentResultCounts = new List();
}
Debug.Assert(
this.segmentContainers.Count == this.segmentNames.Count,
"this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one");
Debug.Assert(
this.segmentContainers.Count == this.segmentResultCounts.Count,
"this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one");
this.segmentNames.Add(name);
this.segmentContainers.Add(container);
this.segmentResultCounts.Add(0);
Debug.Assert(
this.segmentContainers.Count == this.segmentNames.Count,
"this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one");
Debug.Assert(
this.segmentContainers.Count == this.segmentResultCounts.Count,
"this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one");
}
}
/// Supports a by-reference equality comparison for objects.
internal class ObjectReferenceEqualityComparer : IEqualityComparer
{
/// Singleton instance.
private static readonly ObjectReferenceEqualityComparer instance = new ObjectReferenceEqualityComparer();
/// Singleton instance.
internal static ObjectReferenceEqualityComparer Instance
{
get
{
return ObjectReferenceEqualityComparer.instance;
}
}
/// Determines whether the specified objects are equal
/// The first object to compare.
/// The second object to compare.
/// true if the specified objects are equal; otherwise, false.
bool IEqualityComparer.Equals(object x, object y)
{
return object.ReferenceEquals(x, y);
}
/// Returns a hash code for the specified object.
/// The Object for which a hash code is to be returned.
/// A hash code for the specified object.
int IEqualityComparer.GetHashCode(object obj)
{
Debug.Assert(obj != null, "obj != null");
return obj.GetHashCode();
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// An abstract base serializer for various serializers
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Serializers
{
#region Namespaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services.Providers;
using System.Diagnostics;
using System.IO;
using System.Text;
#endregion Namespaces.
/// Abstract base class for all serializers.
internal abstract class Serializer : IExceptionWriter
{
#region Private fields.
/// Base URI from which resources should be resolved.
private readonly Uri absoluteServiceUri;
/// Configuration for the service.
private readonly DataServiceConfiguration configuration;
/// Data provider from which metadata should be gathered.
private readonly string httpETagHeaderValue;
/// Data provider from which metadata should be gathered.
private readonly IDataServiceProvider provider;
/// Description for the requested results.
private readonly RequestDescription requestDescription;
/// Collection of complex types, used for cycle detection.
private HashSet complexTypeCollection;
/// Resolved segment containers.
private List segmentContainers;
/// Segment names.
private List segmentNames;
/// Result counts for segments.
private List segmentResultCounts;
#endregion Private fields.
/// Initializes a new base Serializer, ready to write out a description.
/// Description for the requested results.
/// Base URI from which resources should be resolved.
/// Service with configuration and provider from which metadata should be gathered.
/// HTTP ETag header value.
internal Serializer(RequestDescription requestDescription, Uri absoluteServiceUri, IDataService service, string httpETagHeaderValue)
{
Debug.Assert(requestDescription != null, "requestDescription != null");
Debug.Assert(absoluteServiceUri != null, "absoluteServiceUri != null");
Debug.Assert(service != null, "service != null");
this.requestDescription = requestDescription;
this.absoluteServiceUri = absoluteServiceUri;
this.configuration = service.Configuration;
this.provider = service.Provider;
this.httpETagHeaderValue = httpETagHeaderValue;
}
/// Container for the resource being serialized (possibly null).
protected ResourceContainer CurrentContainer
{
get
{
if (this.segmentContainers == null || this.segmentContainers.Count == 0)
{
return this.requestDescription.LastSegmentInfo.TargetContainer;
}
else
{
return this.segmentContainers[this.segmentContainers.Count - 1];
}
}
}
///
/// Gets the Data provider from which metadata should be gathered.
///
protected IDataServiceProvider Provider
{
[DebuggerStepThrough]
get { return this.provider; }
}
/// Gets the absolute URI to the service.
protected Uri AbsoluteServiceUri
{
[DebuggerStepThrough]
get { return this.absoluteServiceUri; }
}
///
/// Gets the RequestDescription for the request that is getting serialized.
///
protected RequestDescription RequestDescription
{
[DebuggerStepThrough]
get
{
return this.requestDescription;
}
}
/// Serializes exception information.
/// Description of exception to serialize.
public abstract void WriteException(HandleExceptionArgs args);
///
/// Gets the uri given the list of resource properties. This logic must be the same across all
/// serializers. Hence putting this in a util class
///
/// instance of the resource type whose properties needs to be returned
/// Provider from which resource was obtained.
/// Container for the resource.
/// Base URI from which resources should be resolved.
/// uri for the given resource
internal static Uri GetUri(object resource, IDataServiceProvider provider, ResourceContainer container, Uri absoluteServiceUri)
{
string objectKey = GetObjectKey(resource, provider, container.Name);
return RequestUriProcessor.AppendEscapedSegment(absoluteServiceUri, objectKey);
}
///
/// Appends the given entry to the given uri
///
/// uri to which the entry needs to be appended
/// entry which gets appended to the given uri
/// new uri with the entry appended to the given uri
internal static Uri AppendEntryToUri(Uri currentUri, string entry)
{
return RequestUriProcessor.AppendUnescapedSegment(currentUri, entry);
}
///
/// Handles the complete serialization for the specified .
///
/// Query results to enumerate.
/// Whether was succesfully advanced to the first element.
///
/// should correspond to the RequestQuery of the
/// RequestDescription object passed while constructing this serializer
/// We allow the results to be passed in
/// to let the query be executed earlier than at result-writing time, which
/// helps detect data and query errors where they can be better handled.
///
internal void WriteRequest(IEnumerator queryResults, bool hasMoved)
{
IEnumerable requestEnumerable = this.requestDescription.RequestEnumerable;
Debug.Assert(requestEnumerable != null, "requestQuery != null");
Debug.Assert(queryResults != null, "queryResults != null");
IExpandedResult expanded = queryResults as IExpandedResult;
if (this.requestDescription.LinkUri)
{
this.PushSegmentForRoot();
if (this.requestDescription.IsSingleResult)
{
this.WriteLink(queryResults.Current);
if (queryResults.MoveNext())
{
throw new InvalidOperationException(Strings.SingleResourceExpected);
}
}
else
{
this.WriteLinkCollection(queryResults, hasMoved);
}
this.PopSegmentName();
}
else if (this.requestDescription.IsSingleResult)
{
Debug.Assert(hasMoved == true, "hasMoved == true");
this.WriteTopLevelElement(expanded, queryResults.Current);
if (queryResults.MoveNext())
{
throw new InvalidOperationException(Strings.SingleResourceExpected);
}
}
else
{
this.WriteTopLevelElements(expanded, queryResults, hasMoved);
}
this.Flush();
}
/// Gets the expandable value for the specified object.
/// Expanded properties for the result, possibly null.
/// Object with value to retrieve.
/// Property for which value will be retrieved.
/// The property value.
protected static object GetExpandedProperty(IExpandedResult expanded, object customObject, ResourceProperty property)
{
Debug.Assert(property != null, "property != null");
if (expanded == null)
{
return property.GetValue(customObject);
}
else
{
return expanded.GetExpandedPropertyValue(property.Name);
}
}
/// Flushes the writer to the underlying stream.
protected abstract void Flush();
/// Writes a single top-level element.
/// Expanded properties for the result.
/// Element to write, possibly null.
protected abstract void WriteTopLevelElement(IExpandedResult expanded, object element);
/// Writes multiple top-level elements, possibly none.
/// Expanded properties for the result.
/// Result elements.
/// Whether was succesfully advanced to the first element.
protected abstract void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved);
///
/// Write out the uri for the given element
///
/// element whose uri needs to be written out.
protected abstract void WriteLink(object element);
///
/// Write out the uri for the given elements
///
/// elements whose uri need to be writtne out
/// the current state of the enumerator.
protected abstract void WriteLinkCollection(IEnumerator elements, bool hasMoved);
///
/// Adds the given object instance to complex type collection
///
/// instance to be added
/// true, if it got added successfully
protected bool AddToComplexTypeCollection(object complexTypeInstance)
{
if (this.complexTypeCollection == null)
{
this.complexTypeCollection = new HashSet(ObjectReferenceEqualityComparer.Instance);
}
return this.complexTypeCollection.Add(complexTypeInstance);
}
/// Gets the for the object.
/// Object being written.
/// Resource container for current object.
/// This method relies on segments being pushed and popped for MEST scenarios.
protected ResourceContainer GetContainerForCurrent(object current)
{
Debug.Assert(current != null, "current != null");
ResourceContainer result = this.CurrentContainer;
if (result == null)
{
result = this.Provider.GetContainerForResourceType(current.GetType());
}
return result;
}
#if ASTORIA_OPEN_OBJECT
/// Pushes a segment for the root of the tree being written out.
/// Name of open property.
/// Resulved type of open property.
/// Calls to this method should be balanced with calls to PopSegmentName.
protected void PushSegmentForOpenProperty(string propertyName, ResourceType propertyResourceType)
{
Debug.Assert(propertyName != null, "propertyName != null");
Debug.Assert(propertyResourceType != null, "propertyResourceType != null");
ResourceContainer container = this.provider.GetContainerForResourceType(propertyResourceType.Type);
this.PushSegment(propertyName, container);
}
#endif
/// Increments the result count for the current segment, throws if exceeds the limit.
protected void IncrementSegmentResultCount()
{
if (this.segmentResultCounts != null)
{
Debug.Assert(this.segmentResultCounts.Count > 0, "this.segmentResultCounts.Count > 0 -- otherwise we didn't PushSegmentForRoot");
int max = this.configuration.MaxResultsPerCollection;
if (max != Int32.MaxValue)
{
int count = this.segmentResultCounts[this.segmentResultCounts.Count - 1];
checked
{
count++;
}
if (count > max)
{
throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError);
}
this.segmentResultCounts[this.segmentResultCounts.Count - 1] = count;
}
}
}
/// Pushes a segment from the stack of names being written.
/// Property to push.
/// Calls to this method should be balanced with calls to PopSegmentName.
protected void PushSegmentForProperty(ResourceProperty property)
{
Debug.Assert(property != null, "property != null");
ResourceContainer current = null;
if ((property.Kind & (ResourcePropertyKind.ResourceReference | ResourcePropertyKind.ResourceSetReference)) != 0)
{
current = this.CurrentContainer;
if (current != null)
{
current = this.provider.GetContainer(current.Name, current.ResourceType.Type, property);
}
}
this.PushSegment(property.Name, current);
}
/// Pushes a segment for the root of the tree being written out.
/// Calls to this method should be balanced with calls to PopSegmentName.
protected void PushSegmentForRoot()
{
this.PushSegment(this.RequestDescription.ContainerName, this.CurrentContainer);
}
/// Pops a segment name from the stack of names being written.
/// Calls to this method should be balanced with previous calls to PushSegmentName.
protected void PopSegmentName()
{
if (this.segmentNames != null)
{
Debug.Assert(this.segmentNames.Count > 0, "this.segmentNames.Count > 0");
this.segmentNames.RemoveAt(this.segmentNames.Count - 1);
this.segmentContainers.RemoveAt(this.segmentContainers.Count - 1);
this.segmentResultCounts.RemoveAt(this.segmentResultCounts.Count - 1);
Debug.Assert(
this.segmentContainers.Count == this.segmentNames.Count,
"this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one");
Debug.Assert(
this.segmentContainers.Count == this.segmentResultCounts.Count,
"this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one");
}
}
/// Returns a clone of the segment names and containers trackd at this moment.
/// A clone of the segment names and containers tracked at this moment; possibly null.
protected object SaveSegmentNames()
{
if (this.segmentNames == null)
{
return null;
}
else
{
return new object[3]
{
new List(this.segmentNames),
new List(this.segmentContainers),
new List(this.segmentResultCounts)
};
}
}
/// Restores the segment names saved through .
/// Value returned from a previous call to .
protected void RestoreSegmentNames(object savedSegmentNames)
{
object[] savedLists = (object[])savedSegmentNames;
if (savedLists == null)
{
this.segmentNames = null;
this.segmentContainers = null;
this.segmentResultCounts = null;
}
else
{
this.segmentNames = (List)savedLists[0];
this.segmentContainers = (List)savedLists[1];
this.segmentResultCounts = (List)savedLists[2];
}
}
///
/// Remove the given object instance from the complex type collection
///
/// instance to be removed
protected void RemoveFromComplexTypeCollection(object complexTypeInstance)
{
Debug.Assert(this.complexTypeCollection != null, "this.complexTypeCollection != null");
Debug.Assert(this.complexTypeCollection.Contains(complexTypeInstance), "this.complexTypeCollection.Contains(complexTypeInstance)");
this.complexTypeCollection.Remove(complexTypeInstance);
}
/// Checks whether the property with the specified name should be expanded in-line.
/// Name of property to consider for expansion.
/// true if the segment should be expanded; false otherwise.
protected bool ShouldExpandSegment(string name)
{
Debug.Assert(name != null, "name != null");
if (this.segmentNames == null)
{
return false;
}
for (int i = 0; i < this.requestDescription.ExpandPaths.Count; i++)
{
List expandPath = this.requestDescription.ExpandPaths[i];
if (expandPath.Count < this.segmentNames.Count)
{
continue;
}
// We start off at '1' for segment names because the first one is the
// "this" in the query (/Customers?$expand=Orders doesn't include "Customers").
bool matchFound = true;
for (int j = 1; j < this.segmentNames.Count; j++)
{
if (expandPath[j - 1].Name != this.segmentNames[j])
{
matchFound = false;
break;
}
}
if (matchFound && expandPath[this.segmentNames.Count - 1].Name == name)
{
return true;
}
}
return false;
}
///
/// Returns the ETag value from the host response header
///
/// resource whose etag value gets to be returned
/// returns the etag value for the given resource
protected string GetETagValue(object resource)
{
if (this.requestDescription.IsSingleResult)
{
return this.httpETagHeaderValue;
}
else
{
return WebUtil.GetETagValue(this.provider, resource, this.GetContainerForCurrent(resource));
}
}
/// Returns the key for the given resource.
/// Resource for which key value needs to be returned.
/// Specific provider from which resource was obtained.
/// name of the entity container that the resource belongs to
/// Key for the given resource, with values encoded for use in a URI.
private static string GetObjectKey(object resource, IDataServiceProvider provider, string containerName)
{
Debug.Assert(resource != null, "resource != null");
Debug.Assert(provider != null, "provider != null");
Debug.Assert(!String.IsNullOrEmpty(containerName), "container name must be specified");
StringBuilder resultBuilder = new StringBuilder();
resultBuilder.Append(containerName);
resultBuilder.Append('(');
ResourceType resourceType = provider.GetResourceType(resource.GetType());
Debug.Assert(resourceType != null, "resourceType != null");
List keyProperties = resourceType.KeyProperties;
Debug.Assert(keyProperties.Count != 0, "every resource type must have a key");
for (int i = 0; i < keyProperties.Count; i++)
{
ResourceProperty property = keyProperties[i];
Debug.Assert(property.IsOfKind(ResourcePropertyKind.Key), "must be key property");
object keyValue = property.GetValue(resource);
if (keyValue == null)
{
// null keys not supported.
throw new InvalidOperationException(Strings.Serializer_NullKeysAreNotSupported(property.Name));
}
if (i == 0)
{
if (keyProperties.Count != 1)
{
resultBuilder.Append(property.Name);
resultBuilder.Append('=');
}
}
else
{
resultBuilder.Append(',');
resultBuilder.Append(property.Name);
resultBuilder.Append('=');
}
string keyValueText;
if (!System.Data.Services.Parsing.WebConvert.TryKeyPrimitiveToString(keyValue, out keyValueText))
{
throw new InvalidOperationException(Strings.Serializer_CannotConvertValue(keyValue));
}
Debug.Assert(keyValueText != null, "keyValueText != null - otherwise TryKeyPrimitiveToString returned true and null value");
resultBuilder.Append(System.Uri.EscapeDataString(keyValueText));
}
resultBuilder.Append(')');
return resultBuilder.ToString();
}
/// Pushes a segment from the stack of names being written.
/// Name of property to push.
/// Container to push (possibly null).
/// Calls to this method should be balanced with calls to PopSegmentName.
private void PushSegment(string name, ResourceContainer container)
{
if (this.configuration.MaxResultsPerCollection != Int32.MaxValue ||
(this.requestDescription.ExpandPaths != null &&
this.requestDescription.ExpandPaths.Count > 0))
{
if (this.segmentNames == null)
{
this.segmentNames = new List();
this.segmentContainers = new List();
this.segmentResultCounts = new List();
}
Debug.Assert(
this.segmentContainers.Count == this.segmentNames.Count,
"this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one");
Debug.Assert(
this.segmentContainers.Count == this.segmentResultCounts.Count,
"this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one");
this.segmentNames.Add(name);
this.segmentContainers.Add(container);
this.segmentResultCounts.Add(0);
Debug.Assert(
this.segmentContainers.Count == this.segmentNames.Count,
"this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one");
Debug.Assert(
this.segmentContainers.Count == this.segmentResultCounts.Count,
"this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one");
}
}
/// Supports a by-reference equality comparison for objects.
internal class ObjectReferenceEqualityComparer : IEqualityComparer
{
/// Singleton instance.
private static readonly ObjectReferenceEqualityComparer instance = new ObjectReferenceEqualityComparer();
/// Singleton instance.
internal static ObjectReferenceEqualityComparer Instance
{
get
{
return ObjectReferenceEqualityComparer.instance;
}
}
/// Determines whether the specified objects are equal
/// The first object to compare.
/// The second object to compare.
/// true if the specified objects are equal; otherwise, false.
bool IEqualityComparer.Equals(object x, object y)
{
return object.ReferenceEquals(x, y);
}
/// Returns a hash code for the specified object.
/// The Object for which a hash code is to be returned.
/// A hash code for the specified object.
int IEqualityComparer.GetHashCode(object obj)
{
Debug.Assert(obj != null, "obj != null");
return obj.GetHashCode();
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.