Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / RequestDescription.cs / 1305376 / RequestDescription.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Provides a description of the request a client has submitted.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services
{
#region Namespaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services.Providers;
using System.Diagnostics;
using System.Linq;
#endregion Namespaces.
///
/// Query Counting Option
///
internal enum RequestQueryCountOption
{
/// Do not count the result set
None,
/// Count and return value inline (together with data)
Inline,
/// Count and return value only (as integer)
ValueOnly
}
///
/// Use this class to describe the data request a client has
/// submitted to the service.
///
[DebuggerDisplay("RequestDescription={TargetSource} '{ContainerName}' -> {TargetKind} '{TargetResourceType}'")]
internal class RequestDescription
{
///
/// The default response version of the data service. If no version is set for a particular response
/// The DataService will respond with this version (1.0)
///
internal static readonly Version DataServiceDefaultResponseVersion = new Version(1, 0);
#region Private fields.
///
/// Default set of known data service versions, currently 1.0 and 2.0
///
private static readonly Version[] KnownDataServiceVersions = new Version[] { new Version(1, 0), new Version(2, 0) };
/// The name of the container for results.
private readonly string containerName;
/// Root of the projection and expansion tree.
/// If this is null - no projections or expansions were part of the request.
private readonly RootProjectionNode rootProjectionNode;
/// The MIME type for the requested resource, if specified.
private readonly string mimeType;
/// URI for the result (without the query component).
private readonly Uri resultUri;
/// SegmentInfo containing information about every segment in the uri
private readonly SegmentInfo[] segmentInfos;
/// Whether the container name should be used to name the result.
private readonly bool usesContainerName;
/// Query count option, whether to count the result set or not, and how
private RequestQueryCountOption countOption;
/// The value of the row count
private long countValue;
/// The minimum client version requirement
private Version requireMinimumVersion;
/// The server response version
private Version responseVersion;
///
/// Max version of features used in the request. We need to distinguish between feature versions and response version since
/// some new features (e.g. Server Projections) do not cause response version to be raised.
///
private Version maxFeatureVersion;
#endregion Private fields.
///
/// Initializes a new RequestDescription for a query specified by the
/// request Uri.
///
/// The kind of target for the request.
/// The source for this target.
/// URI to the results requested (with no query component).
internal RequestDescription(RequestTargetKind targetKind, RequestTargetSource targetSource, Uri resultUri)
{
WebUtil.DebugEnumIsDefined(targetKind);
Debug.Assert(resultUri != null, "resultUri != null");
Debug.Assert(resultUri.IsAbsoluteUri, "resultUri.IsAbsoluteUri(" + resultUri + ")");
SegmentInfo segment = new SegmentInfo();
segment.TargetKind = targetKind;
segment.TargetSource = targetSource;
segment.SingleResult = true;
this.segmentInfos = new SegmentInfo[] { segment };
this.resultUri = resultUri;
this.requireMinimumVersion = new Version(1, 0);
this.responseVersion = DataServiceDefaultResponseVersion;
this.maxFeatureVersion = new Version(1, 0);
}
///
/// Initializes a new RequestDescription for a query specified by the
/// request Uri.
///
/// list containing information about each segment of the request uri
/// Name of the container source.
/// Whether the container name should be used to name the result.
/// The MIME type for the requested resource, if specified.
/// URI to the results requested (with no query component).
internal RequestDescription(
SegmentInfo[] segmentInfos,
string containerName,
bool usesContainerName,
string mimeType,
Uri resultUri)
{
Debug.Assert(segmentInfos != null && segmentInfos.Length != 0, "segmentInfos != null && segmentInfos.Length != 0");
Debug.Assert(resultUri != null, "resultUri != null");
Debug.Assert(resultUri.IsAbsoluteUri, "resultUri.IsAbsoluteUri(" + resultUri + ")");
this.segmentInfos = segmentInfos;
this.containerName = containerName;
this.usesContainerName = usesContainerName;
this.mimeType = mimeType;
this.resultUri = resultUri;
this.requireMinimumVersion = new Version(1, 0);
this.responseVersion = DataServiceDefaultResponseVersion;
this.maxFeatureVersion = new Version(1, 0);
}
/// Initializes a new RequestDescription based on an existing one.
/// Other description to base new description on.
/// Query results for new request description.
/// Projection segment describing the projections on the top level of the query.
internal RequestDescription(
RequestDescription other,
IEnumerable queryResults,
RootProjectionNode rootProjectionNode)
{
Debug.Assert(
queryResults == null || other.SegmentInfos != null,
"queryResults == null || other.SegmentInfos != null -- otherwise there isn't a segment in which to replace the query.");
Debug.Assert(
rootProjectionNode == null || queryResults != null,
"rootProjectionNode == null || queryResults != null -- otherwise there isn't a query to execute and expand");
this.containerName = other.containerName;
this.mimeType = other.mimeType;
this.usesContainerName = other.usesContainerName;
this.resultUri = other.resultUri;
this.segmentInfos = other.SegmentInfos;
this.rootProjectionNode = rootProjectionNode;
this.countOption = other.countOption;
this.SkipTokenExpressionCount = other.SkipTokenExpressionCount;
this.SkipTokenProperties = other.SkipTokenProperties;
this.countValue = other.countValue;
this.requireMinimumVersion = other.requireMinimumVersion;
this.responseVersion = other.responseVersion;
this.maxFeatureVersion = other.maxFeatureVersion;
if (queryResults == null)
{
this.segmentInfos = other.SegmentInfos;
}
else
{
int lastSegmentIndex = other.SegmentInfos.Length - 1;
SegmentInfo lastSegmentInfo = other.SegmentInfos[lastSegmentIndex];
lastSegmentInfo.RequestEnumerable = queryResults;
}
}
/// The name of the container for results.
internal string ContainerName
{
[DebuggerStepThrough]
get { return this.containerName; }
}
/// Root of the projection and expansion tree.
internal RootProjectionNode RootProjectionNode
{
[DebuggerStepThrough]
get { return this.rootProjectionNode; }
}
/// URI for the result (without the query component).
internal Uri ResultUri
{
[DebuggerStepThrough]
get { return this.resultUri; }
}
/// Returns the list containing the information about each segment that make up the request uri
internal SegmentInfo[] SegmentInfos
{
[DebuggerStepThrough]
get { return this.segmentInfos; }
}
/// The base query for the request, before client-specified composition.
internal IEnumerable RequestEnumerable
{
get { return this.LastSegmentInfo.RequestEnumerable; }
}
/// Whether the result of this request is a single element.
internal bool IsSingleResult
{
get { return this.LastSegmentInfo.SingleResult; }
}
/// The MIME type for the requested resource, if specified.
internal string MimeType
{
[DebuggerStepThrough]
get { return this.mimeType; }
}
/// The kind of target being requested.
internal RequestTargetKind TargetKind
{
get { return this.LastSegmentInfo.TargetKind; }
}
/// The type of resource targetted by this request.
internal ResourceType TargetResourceType
{
get { return this.LastSegmentInfo.TargetResourceType; }
}
/// The type of source for the request target.
internal RequestTargetSource TargetSource
{
get { return this.LastSegmentInfo.TargetSource; }
}
///
/// Returns the resource property on which this query is targeted
///
internal ResourceProperty Property
{
get { return this.LastSegmentInfo.ProjectedProperty; }
}
/// Whether the container name should be used to name the result.
internal bool UsesContainerName
{
[DebuggerStepThrough]
get { return this.usesContainerName; }
}
/// Returns the last segment
internal SegmentInfo LastSegmentInfo
{
get { return this.segmentInfos[this.segmentInfos.Length - 1]; }
}
/// Returns true if the request description refers to a link uri. Otherwise returns false.
internal bool LinkUri
{
get
{
return (this.segmentInfos.Length >= 3 && this.segmentInfos[this.segmentInfos.Length - 2].TargetKind == RequestTargetKind.Link);
}
}
/// Returns the request's counting options
internal RequestQueryCountOption CountOption
{
get { return this.countOption; }
set { this.countOption = value; }
}
/// Number of expressions in the $skiptoken for top level expression
internal int SkipTokenExpressionCount
{
get;
set;
}
/// Collection of properties in the $skiptoken for top level expression
internal ICollection SkipTokenProperties
{
get;
set;
}
/// Returns the value of the row count
internal long CountValue
{
get { return this.countValue; }
set { this.countValue = value; }
}
/// The minimum client version requirement
internal Version RequireMinimumVersion
{
get { return this.requireMinimumVersion; }
}
/// The server response version
internal Version ResponseVersion
{
get { return this.responseVersion; }
}
/// Max version of features used in the user's request
internal Version MaxFeatureVersion
{
get { return this.maxFeatureVersion; }
}
///
/// Is the request for an IEnumerable<T> returning service operation.
///
internal bool IsRequestForEnumServiceOperation
{
get
{
return this.TargetSource == RequestTargetSource.ServiceOperation && this.SegmentInfos[0].Operation.ResultKind == ServiceOperationResultKind.Enumeration;
}
}
///
/// Get the single result from the given segment info
///
/// segmentInfo which contains the request query
/// query result as returned by the IQueryable query
internal static IEnumerator GetSingleResultFromEnumerable(SegmentInfo segmentInfo)
{
IEnumerator queryResults = WebUtil.GetRequestEnumerator(segmentInfo.RequestEnumerable);
bool shouldDispose = true;
try
{
WebUtil.CheckResourceExists(queryResults.MoveNext(), segmentInfo.Identifier);
CheckQueryResult(queryResults.Current, segmentInfo);
shouldDispose = false;
return queryResults;
}
finally
{
// Dispose the Enumerator in case of error
if (shouldDispose)
{
WebUtil.Dispose(queryResults);
}
}
}
///
/// Checks query result.
///
/// Query result to be checked.
/// Segment details for the .
internal static void CheckQueryResult(object result, SegmentInfo segmentInfo)
{
// e.g. /Customers(4) - if there is a direct reference to an entity, it should not be null.
// e.g. $value also must not be null, since you are dereferencing the values
// Any other case, having null is fine
if (segmentInfo.IsDirectReference && result == null)
{
throw DataServiceException.CreateResourceNotFound(segmentInfo.Identifier);
}
IEnumerable enumerable;
if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty &&
WebUtil.IsElementIEnumerable(result, out enumerable))
{
throw DataServiceException.CreateSyntaxError(
Strings.InvalidUri_OpenPropertiesCannotBeCollection(segmentInfo.Identifier));
}
}
///
/// Create a new request description from the given request description and new entity as the result.
///
/// Existing request description.
/// entity that needs to be the result of the new request.
/// container to which the entity belongs to.
/// a new instance of request description containing information about the given entity.
internal static RequestDescription CreateSingleResultRequestDescription(
RequestDescription description, object entity, ResourceSetWrapper container)
{
// Create a new request description for the results that will be returned.
SegmentInfo segmentInfo = new SegmentInfo();
segmentInfo.RequestEnumerable = new object[] { entity };
segmentInfo.TargetKind = description.TargetKind;
segmentInfo.TargetSource = description.TargetSource;
segmentInfo.SingleResult = true;
segmentInfo.ProjectedProperty = description.Property;
segmentInfo.TargetResourceType = container != null ? container.ResourceType : null;
segmentInfo.TargetContainer = container;
segmentInfo.Identifier = description.LastSegmentInfo.Identifier;
#if DEBUG
segmentInfo.AssertValid();
#endif
SegmentInfo[] segmentInfos = description.SegmentInfos;
segmentInfos[segmentInfos.Length - 1] = segmentInfo;
RequestDescription resultDescription = new RequestDescription(
segmentInfos,
container != null ? container.Name : null,
description.UsesContainerName,
description.MimeType,
description.ResultUri);
resultDescription.requireMinimumVersion = description.RequireMinimumVersion;
resultDescription.responseVersion = description.ResponseVersion;
resultDescription.maxFeatureVersion = description.MaxFeatureVersion;
return resultDescription;
}
///
/// Checks whether etag headers are allowed (both request and response) for this request.
/// ETag request headers are mainly If-Match and If-None-Match headers
/// ETag response header is written only when its valid to specify one of the above mentioned request headers.
///
/// description about the request uri.
/// true if If-Match or If-None-Match are allowed request headers for this request, otherwise false.
internal static bool IsETagHeaderAllowed(RequestDescription description)
{
// IfMatch and IfNone match request headers are allowed and etag response header must be written
// only when the request targets a single resource, which does not have $count and $links segment and there are no $expands query option specified.
return description.IsSingleResult && description.CountOption != RequestQueryCountOption.ValueOnly && (description.RootProjectionNode == null || !description.RootProjectionNode.ExpansionsSpecified) && !description.LinkUri;
}
///
/// Verify that the request version is a version we know.
///
/// request version from the header
/// returns true if the request version is known
internal static bool IsKnownRequestVersion(Version requestVersion)
{
return KnownDataServiceVersions.Contains(requestVersion);
}
///
/// Raise the feature version if the target container contains any type with FF mapped properties that is KeepInContent=false.
///
/// service instance
/// This RequestDescription instance
internal RequestDescription UpdateAndCheckEpmFeatureVersion(IDataService service)
{
Debug.Assert(this.LastSegmentInfo != null, "this.LastSegmentInfo != null");
if (this.LinkUri)
{
// For $link operations we raise the feature version if either side of $link points to a set that
// is not V1 compatible.
ResourceSetWrapper leftSet;
ResourceSetWrapper rightSet;
this.GetLinkedResourceSets(out leftSet, out rightSet);
Debug.Assert(leftSet != null, "leftSet != null");
Debug.Assert(rightSet != null, "rightSet != null");
this.UpdateAndCheckEpmFeatureVersion(leftSet, service);
this.UpdateAndCheckEpmFeatureVersion(rightSet, service);
}
else
{
// The last segment might not be a resource. Trace backward to find the target entity resource.
int resourceIndex = this.GetIndexOfTargetEntityResource();
// Not every request has a target resource. Service Operations for example can return just primitives.
if (resourceIndex != -1)
{
ResourceSetWrapper resourceSet = this.SegmentInfos[resourceIndex].TargetContainer;
Debug.Assert(resourceSet != null, "resourceSet != null");
this.UpdateAndCheckEpmFeatureVersion(resourceSet, service);
}
}
return this;
}
///
/// Raise the feature version if the given set contains any type with FF mapped properties that is KeepInContent=false.
///
/// Resource set to test
/// service instance
/// This RequestDescription instance
internal RequestDescription UpdateAndCheckEpmFeatureVersion(ResourceSetWrapper resourceSet, IDataService service)
{
Debug.Assert(resourceSet != null, "resourceSet != null");
Debug.Assert(service != null, "provider != null");
// For feature version we only look at the SET, and bump the version if it's not V1 compatible. We do this even if the
// target kind is not Resource. So we could have request and response DSV be 1.0 while feature version be 2.0.
if (!resourceSet.EpmIsV1Compatible(service.Provider))
{
this.RaiseFeatureVersion(2, 0, service.Configuration);
}
return this;
}
/// Updates the response version based on response format and the target resource type
/// text for Accepts header content required to infere response format
/// data service provider instance
/// The instance for which the update of response version happens
internal RequestDescription UpdateEpmResponseVersion(string acceptTypesText, DataServiceProviderWrapper provider)
{
return this.UpdateEpmResponseVersion(acceptTypesText, this.LastSegmentInfo.TargetContainer, provider);
}
/// Updates the response version based on response format and given resource type
/// text for Accepts header content required to infere response format
/// resourceSet to check for friendly feeds presence
/// data service provider instance
/// The instance for which the update of response version happens
internal RequestDescription UpdateEpmResponseVersion(string acceptTypesText, ResourceSetWrapper resourceSet, DataServiceProviderWrapper provider)
{
Debug.Assert(provider != null, "provider != null");
// Response will be 2.0 if any of the property in the types contained in the resource set has KeepInContent false
if (this.TargetKind == RequestTargetKind.Resource)
{
Debug.Assert(resourceSet != null, "Must have valid resource set");
if (!resourceSet.EpmIsV1Compatible(provider) && !this.LinkUri)
{
// Friendly feeds must only bump the response version for Atom responses
if (WebUtil.IsAtomMimeType(acceptTypesText))
{
this.RaiseResponseVersion(2, 0);
}
}
}
return this;
}
///
/// Returns the last segment info whose target request kind is resource
///
/// The index of the parent resource
internal int GetIndexOfTargetEntityResource()
{
Debug.Assert(this.segmentInfos.Length >= 1, "this.segmentInfos.Length >= 1");
int result = -1;
if (this.LinkUri || this.CountOption == RequestQueryCountOption.ValueOnly)
{
return this.SegmentInfos.Length - 1;
}
for (int j = this.SegmentInfos.Length - 1; j >= 0; j--)
{
if (this.segmentInfos[j].TargetKind == RequestTargetKind.Resource || this.segmentInfos[j].HasKeyValues)
{
result = j;
break;
}
}
return result;
}
///
/// Raise the minimum client version requirement for this request
///
/// The major segment of the version
/// The minor segment of the version
internal void RaiseMinimumVersionRequirement(int major, int minor)
{
this.requireMinimumVersion = RaiseVersion(this.requireMinimumVersion, major, minor);
// Each time we bump the request version we need to bump the max feature version as well.
// Note that sometimes we need to bump max feature version even if request version is not raised.
this.maxFeatureVersion = RaiseVersion(this.maxFeatureVersion, major, minor);
}
///
/// Raise the response version for this request
///
/// The major segment of the version
/// The minor segment of the version
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811", Justification = "Will be used for other 2.0 server features")]
internal void RaiseResponseVersion(int major, int minor)
{
this.responseVersion = RaiseVersion(this.responseVersion, major, minor);
// Each time we bump the response version we need to bump the max feature version as well.
// Note that sometimes we need to bump max feature version even if response version is not raised.
this.maxFeatureVersion = RaiseVersion(this.maxFeatureVersion, major, minor);
}
///
/// Raise the version for features used in the user's request
///
/// The major segment of the version
/// The minor segment of the version
/// Data service configuration instance to validate the feature version.
internal void RaiseFeatureVersion(int major, int minor, DataServiceConfiguration config)
{
this.maxFeatureVersion = RaiseVersion(this.maxFeatureVersion, major, minor);
config.ValidateMaxProtocolVersion(this);
}
///
/// If necessary raises version to the version requested by the user.
///
/// Version to raise.
/// The major segment of the new version
/// The minor segment of the new version
/// New version if the requested version is greater than the existing version.
private static Version RaiseVersion(Version versionToRaise, int major, int minor)
{
if (major > versionToRaise.Major ||
(major == versionToRaise.Major && minor > versionToRaise.Minor))
{
versionToRaise = new Version(major, minor);
}
return versionToRaise;
}
///
/// Returns the resource sets on the left and right hand sides of $link.
///
/// Resource set to the left of $link.
/// Resource set to the right of $link.
private void GetLinkedResourceSets(out ResourceSetWrapper leftSet, out ResourceSetWrapper rightSet)
{
Debug.Assert(this.LinkUri, "GetLinkedResourceSets should only be called if this is a $link request.");
int idx = 0;
for (; idx < this.segmentInfos.Length; idx++)
{
if (this.segmentInfos[idx].TargetKind == RequestTargetKind.Link)
{
break;
}
}
Debug.Assert(idx > 0 && idx < this.segmentInfos.Length - 1, "idx > 0 && idx < this.segmentInfos.Length - 1");
leftSet = this.segmentInfos[idx - 1].TargetContainer;
rightSet = this.segmentInfos[idx + 1].TargetContainer;
}
}
}
// 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
- ManagementObjectCollection.cs
- Visitor.cs
- PropertyManager.cs
- StreamInfo.cs
- StorageFunctionMapping.cs
- PriorityChain.cs
- PropertyContainer.cs
- WsiProfilesElementCollection.cs
- HttpClientCertificate.cs
- BindingFormattingDialog.cs
- CannotUnloadAppDomainException.cs
- MarginCollapsingState.cs
- UndoEngine.cs
- OutputCacheEntry.cs
- ReverseInheritProperty.cs
- ButtonRenderer.cs
- WindowsGraphics2.cs
- ImmutablePropertyDescriptorGridEntry.cs
- MasterPageBuildProvider.cs
- FixedNode.cs
- SortFieldComparer.cs
- MessageHeaderInfoTraceRecord.cs
- DataGridViewColumnConverter.cs
- HttpApplication.cs
- ServiceErrorHandler.cs
- ControlCachePolicy.cs
- ExpressionConverter.cs
- ScriptControl.cs
- MemberProjectedSlot.cs
- GcHandle.cs
- IOThreadTimer.cs
- OleServicesContext.cs
- DetailsViewUpdateEventArgs.cs
- HttpApplication.cs
- ISO2022Encoding.cs
- Configuration.cs
- PersistChildrenAttribute.cs
- InputBinding.cs
- MetadataPropertyvalue.cs
- TargetParameterCountException.cs
- AddInActivator.cs
- CompositeScriptReference.cs
- DoubleAnimationUsingPath.cs
- RouteParametersHelper.cs
- InlineObject.cs
- ReadOnlyDataSourceView.cs
- KeyEvent.cs
- SchemaExporter.cs
- RoleService.cs
- FlagsAttribute.cs
- TextUtf8RawTextWriter.cs
- LogStream.cs
- _CookieModule.cs
- FrameworkTextComposition.cs
- DataStreams.cs
- ListDesigner.cs
- LicFileLicenseProvider.cs
- SQLSingle.cs
- XpsViewerException.cs
- RuntimeHandles.cs
- BufferedOutputStream.cs
- Font.cs
- Array.cs
- RelationalExpressions.cs
- dbdatarecord.cs
- FunctionOverloadResolver.cs
- HttpBufferlessInputStream.cs
- ipaddressinformationcollection.cs
- CfgParser.cs
- MeshGeometry3D.cs
- PermissionSet.cs
- CreateUserWizardStep.cs
- MultiBinding.cs
- InboundActivityHelper.cs
- FileDialogCustomPlacesCollection.cs
- RegexCode.cs
- ListMarkerLine.cs
- StyleSheet.cs
- GeometryDrawing.cs
- SQLBinaryStorage.cs
- QilTernary.cs
- SQLInt32.cs
- SetterTriggerConditionValueConverter.cs
- WebPartEditorCancelVerb.cs
- ToolBarButtonClickEvent.cs
- HashCodeCombiner.cs
- TypeDescriptor.cs
- NotFiniteNumberException.cs
- AesCryptoServiceProvider.cs
- SingleAnimationUsingKeyFrames.cs
- CustomError.cs
- SqlBuilder.cs
- Html32TextWriter.cs
- PersonalizationEntry.cs
- BamlRecordHelper.cs
- DataGridRowClipboardEventArgs.cs
- WebServiceFault.cs
- Privilege.cs
- StrokeCollectionDefaultValueFactory.cs
- ProviderCollection.cs