Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / Providers / DataServiceStreamProviderWrapper.cs / 1305376 / DataServiceStreamProviderWrapper.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This wrapper class forwards calls to the underlying IDataServiceStreamProvider
// instance and validates responses from it.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Providers
{
using System;
using System.IO;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Data.Services;
///
/// Wrapper class to forward calls to the underlying IDataServiceStreamProvider instance and validates responses from it.
///
internal class DataServiceStreamProviderWrapper
{
#region Private Fields
///
/// Default buffer size used for stream copy.
///
private const int DefaultBufferSize = 64 * 1024;
///
/// Stream provider instance
///
private IDataServiceStreamProvider streamProvider;
///
/// Data service instance
///
private IDataService dataService;
#endregion Private Fields
#region Constructor
///
/// Constructs the wrapper class for IDataServiceStreamProvider
///
/// Data service instance
public DataServiceStreamProviderWrapper(IDataService dataService)
{
Debug.Assert(dataService != null, "dataService != null");
this.dataService = dataService;
}
#endregion Constructor
#region Public Properties
///
/// Gets buffer size the data service will use when reading from read stream or writing to the write stream.
/// If the size is less than or equals to 0, the default of 64k will be used.
///
public int StreamBufferSize
{
get
{
int size = this.StreamProvider.StreamBufferSize;
return size > 0 ? size : DataServiceStreamProviderWrapper.DefaultBufferSize;
}
}
#endregion Public Properties
#region Private Properties
///
/// Asks the service for an IDataServiceStreamProvider implementation
///
private IDataServiceStreamProvider StreamProvider
{
get
{
if (this.streamProvider == null)
{
this.streamProvider = LoadStreamProvider(this.dataService);
Debug.Assert(this.streamProvider != null, "this.streamProvider != null");
}
return this.streamProvider;
}
}
#endregion Private Properties
#region Internal Methods
///
/// Take the given Media Link Entry uri, and construct the default Edit Media Uri.
///
/// Uri to the Media Link Entry.
/// Uri to the Media Resource.
internal static string GetStreamEditMediaUri(string mediaLinkEntryUri)
{
Debug.Assert(!string.IsNullOrEmpty(mediaLinkEntryUri), "!string.IsNullOrEmpty(mediaLinkEntryUri)");
string result = mediaLinkEntryUri;
if (!result.EndsWith(XmlConstants.UriValueSegment, StringComparison.Ordinal))
{
if (!result.EndsWith("/", StringComparison.Ordinal))
{
result += "/";
}
result += XmlConstants.UriValueSegment;
}
return result;
}
///
/// Asks the data service for a stream provider instance. Throw if none is implemented.
///
/// data service instance
/// stream provider instance
internal static IDataServiceStreamProvider LoadStreamProvider(IDataService dataService)
{
IDataServiceStreamProvider streamProvider = dataService.Provider.GetService(dataService);
if (streamProvider == null)
{
throw new DataServiceException(500, Strings.DataServiceStreamProviderWrapper_MustImplementIDataServiceStreamProviderToSupportStreaming);
}
return streamProvider;
}
///
/// This method is invoked by the data services framework to retrieve the default stream associated
/// with the Entity Type specified by the parameter.
/// Note that we set the response ETag in the host object before we return.
///
/// The stream returned should be the default stream associated with this entity.
/// A reference to the context for the current operation.
/// A valid stream the data service use to query / read a streamed BLOB which is associated with the .
internal Stream GetReadStream(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
string etagFromHeader;
bool? checkETagForEquality;
DataServiceStreamProviderWrapper.GetETagFromHeaders(operationContext, out etagFromHeader, out checkETagForEquality);
Debug.Assert(
string.IsNullOrEmpty(etagFromHeader) && !checkETagForEquality.HasValue || !string.IsNullOrEmpty(etagFromHeader) && checkETagForEquality.HasValue,
"etag and checkETagForEquality parameters must both be set or not set at the same time.");
Stream readStream = null;
try
{
readStream = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetReadStream", () => this.StreamProvider.GetReadStream(entity, etagFromHeader, checkETagForEquality, operationContext), operationContext);
}
catch (DataServiceException e)
{
if (e.StatusCode == (int)System.Net.HttpStatusCode.NotModified)
{
// For status code 304, we MUST set the etag value. Our Error handler will translate
// DataServiceException(304) to a normal response with status code 304 and an empty message-body.
#if DEBUG
WebUtil.WriteETagValueInResponseHeader(null, this.GetStreamETag(entity, operationContext), operationContext.Host);
#else
WebUtil.WriteETagValueInResponseHeader(this.GetStreamETag(entity, operationContext), operationContext.Host);
#endif
}
throw;
}
try
{
if (readStream == null || !readStream.CanRead)
{
throw new InvalidOperationException(Strings.DataService_InvalidStreamFromGetReadStream);
}
// GetStreamETag can throw and we need to catch and dispose the stream.
#if DEBUG
WebUtil.WriteETagValueInResponseHeader(null, this.GetStreamETag(entity, operationContext), operationContext.Host);
#else
WebUtil.WriteETagValueInResponseHeader(this.GetStreamETag(entity, operationContext), operationContext.Host);
#endif
}
catch
{
WebUtil.Dispose(readStream);
throw;
}
return readStream;
}
///
/// This method is invoked by the data services framework whenever an insert or update operation is
/// being processed for the stream associated with the Entity Type specified via the entity parameter.
///
/// The stream returned should be the default stream associated with this entity.
/// A reference to the context for the current operation.
/// A valid stream the data service use to write the contents of a BLOB which is associated with .
internal Stream GetWriteStream(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
string etag;
bool? checkETagForEquality;
DataServiceStreamProviderWrapper.GetETagFromHeaders(operationContext, out etag, out checkETagForEquality);
Debug.Assert(
string.IsNullOrEmpty(etag) && !checkETagForEquality.HasValue || !string.IsNullOrEmpty(etag) && checkETagForEquality.HasValue,
"etag and checkETagForEquality parameters must both be set or not set at the same time.");
Stream writeStream = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetWriteStream", () => this.StreamProvider.GetWriteStream(entity, etag, checkETagForEquality, operationContext), operationContext);
if (writeStream == null || !writeStream.CanWrite)
{
WebUtil.Dispose(writeStream);
throw new InvalidOperationException(Strings.DataService_InvalidStreamFromGetWriteStream);
}
return writeStream;
}
///
/// This method is invoked by the data services framework whenever an delete operation is being processed for the stream associated with
/// the Entity Type specified via the entity parameter.
///
/// The stream deleted should be the default stream associated with this entity.
/// A reference to the context for the current operation.
internal void DeleteStream(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.DeleteStream", () => { this.StreamProvider.DeleteStream(entity, operationContext); return true; }, operationContext);
}
///
/// This method is invoked by the data services framework to obtain the IANA content type (aka media type) of the stream associated
/// with the specified entity. This metadata is needed when constructing the payload for the Media Link Entry associated with the
/// stream (aka Media Resource) or setting the Content-Type HTTP response header.
///
/// The entity associated with the stream for which the content type is to be obtained
/// A reference to the context for the current operation.
/// Valid Content-Type string for the stream associated with the entity
internal string GetStreamContentType(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
string contentType = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetStreamContentType", () => this.StreamProvider.GetStreamContentType(entity, operationContext), operationContext);
if (string.IsNullOrEmpty(contentType))
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_GetStreamContentTypeReturnsEmptyOrNull);
}
return contentType;
}
///
/// This method is invoked by the data services framework to obtain the URI clients should use when making retrieve (ie. GET)
/// requests to the stream(ie. Media Resource). This metadata is needed when constructing the payload for the Media Link Entry
/// associated with the stream (aka Media Resource).
///
/// If IDataServiceStreamProvider.GetReadStreamUri returns a valid Uri, we return that as the Uri to the Media Resource.
/// Otherwise we take the given Media Link Entry uri, and construct the default Media Resource Uri.
///
/// The entity associated with the stream for which a “read stream” is to be obtained
/// A reference to the context for the current operation.
/// Uri to the Media Link Entry.
/// The URI clients should use when making retrieve (ie. GET) requests to the stream(ie. Media Resource).
internal Uri GetReadStreamUri(object entity, DataServiceOperationContext operationContext, string mediaLinkEntryUri)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
Uri readStreamUri = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetReadStreamUri", () => this.StreamProvider.GetReadStreamUri(entity, operationContext), operationContext);
if (readStreamUri != null)
{
if (!readStreamUri.IsAbsoluteUri)
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_GetReadStreamUriMustReturnAbsoluteUriOrNull);
}
else
{
return readStreamUri;
}
}
else
{
return new Uri(DataServiceStreamProviderWrapper.GetStreamEditMediaUri(mediaLinkEntryUri), UriKind.RelativeOrAbsolute);
}
}
///
/// This method is invoked by the data services framework to obtain the ETag of the stream associated with the entity specified.
/// This metadata is needed when constructing the payload for the Media Link Entry associated with the stream (aka Media Resource)
/// as well as to be used as the value of the ETag HTTP response header.
///
/// The entity associated with the stream for which an etag is to be obtained
/// A reference to the context for the current operation.
/// ETag of the stream associated with the entity specified
internal string GetStreamETag(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
string etag = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetStreamETag", () => this.StreamProvider.GetStreamETag(entity, operationContext), operationContext);
if (!WebUtil.IsETagValueValid(etag, true))
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_GetStreamETagReturnedInvalidETagFormat);
}
return etag;
}
///
/// This method is invoked by the data services framework when a request is received to insert into an Entity Set with an associated
/// Entity Type hierarchy that has > 1 Entity Type and >= 1 Entity Type which is tagged as an MLE (ie. includes a stream).
///
/// Fully qualified name entity set name.
/// Data service instance.
///
/// Namespace qualified type name which represents the type the Astoria framework should instantiate to create the MLE associated
/// with the BLOB/MR being inserted.
///
internal ResourceType ResolveType(string entitySetName, IDataService service)
{
DataServiceOperationContext operationContext = service.OperationContext;
Debug.Assert(operationContext != null, "operationContext != null");
string resourceTypeName = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.ResolveType", () => this.StreamProvider.ResolveType(entitySetName, operationContext), operationContext);
if (string.IsNullOrEmpty(resourceTypeName))
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_ResolveTypeMustReturnValidResourceTypeName);
}
ResourceType resourceType = service.Provider.TryResolveResourceType(resourceTypeName);
if (resourceType == null)
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_ResolveTypeMustReturnValidResourceTypeName);
}
return resourceType;
}
///
/// Gets the ETag, ReadStreamUri and ContentType of the stream
///
/// MLE instance
/// context of the current operation
/// Uri to the MLE
/// returns the etag for the stream
/// returns the read stream uri
/// returns the content type of the stream
internal void GetStreamDescription(object entity, DataServiceOperationContext operationContext, string mediaLinkEntryUri, out string etag, out Uri readStreamUri, out string contentType)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
// Call order is part of our contract, do not change it.
etag = this.GetStreamETag(entity, operationContext);
readStreamUri = this.GetReadStreamUri(entity, operationContext, mediaLinkEntryUri);
contentType = this.GetStreamContentType(entity, operationContext);
}
///
/// Dispose the stream provider instance
///
internal void DisposeProvider()
{
if (this.streamProvider != null)
{
WebUtil.Dispose(this.streamProvider);
this.streamProvider = null;
}
}
#endregion Public Methods
#region Private Methods
///
/// Get the ETag header value from the request headers.
///
/// A reference to the context for the current operation.
///
/// The etag value sent by the client (as the value of an If[-None-]Match header) as part of the HTTP request sent to the data service
/// This parameter will be null if no If[-None-]Match header was present
///
///
/// True if an value of the etag parameter was sent to the server as the value of an If-Match HTTP request header
/// False if an value of the etag parameter was sent to the server as the value of an If-None-Match HTTP request header
/// null if the HTTP request for the stream was not a conditional request
///
private static void GetETagFromHeaders(DataServiceOperationContext operationContext, out string etag, out bool? checkETagForEquality)
{
Debug.Assert(operationContext != null, "operationContext != null");
Debug.Assert(operationContext.Host != null, "operationContext.Host != null");
DataServiceHostWrapper host = operationContext.Host;
Debug.Assert(string.IsNullOrEmpty(host.RequestIfMatch) || string.IsNullOrEmpty(host.RequestIfNoneMatch), "IfMatch and IfNoneMatch should not be both set.");
if (string.IsNullOrEmpty(host.RequestIfMatch) && string.IsNullOrEmpty(host.RequestIfNoneMatch))
{
etag = null;
checkETagForEquality = null;
}
else if (!string.IsNullOrEmpty(host.RequestIfMatch))
{
etag = host.RequestIfMatch;
checkETagForEquality = true;
}
else
{
etag = host.RequestIfNoneMatch;
checkETagForEquality = false;
}
}
///
/// Invokes an API call and verifies the response Content-Type and ETag headers are not being modified by the API call.
///
/// Return type from the API call
/// API name
/// Delegate to be called
/// A reference to the context for the current operation.
/// Returns the result from the api call
private static T InvokeApiCallAndValidateHeaders(string methodName, Func apiCall, DataServiceOperationContext operationContext)
{
Debug.Assert(!string.IsNullOrEmpty(methodName), "!string.IsNullOrEmpty(methodName)");
Debug.Assert(operationContext != null, "operationContext != null");
Debug.Assert(apiCall != null, "apiCall != null");
string responseContentType = operationContext.Host.ResponseContentType;
string responseETag = operationContext.Host.ResponseETag;
T result = apiCall();
if (operationContext.Host.ResponseContentType != responseContentType || operationContext.Host.ResponseETag != responseETag)
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_MustNotSetContentTypeAndEtag(methodName));
}
return result;
}
#endregion Private Methods
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This wrapper class forwards calls to the underlying IDataServiceStreamProvider
// instance and validates responses from it.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Providers
{
using System;
using System.IO;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Data.Services;
///
/// Wrapper class to forward calls to the underlying IDataServiceStreamProvider instance and validates responses from it.
///
internal class DataServiceStreamProviderWrapper
{
#region Private Fields
///
/// Default buffer size used for stream copy.
///
private const int DefaultBufferSize = 64 * 1024;
///
/// Stream provider instance
///
private IDataServiceStreamProvider streamProvider;
///
/// Data service instance
///
private IDataService dataService;
#endregion Private Fields
#region Constructor
///
/// Constructs the wrapper class for IDataServiceStreamProvider
///
/// Data service instance
public DataServiceStreamProviderWrapper(IDataService dataService)
{
Debug.Assert(dataService != null, "dataService != null");
this.dataService = dataService;
}
#endregion Constructor
#region Public Properties
///
/// Gets buffer size the data service will use when reading from read stream or writing to the write stream.
/// If the size is less than or equals to 0, the default of 64k will be used.
///
public int StreamBufferSize
{
get
{
int size = this.StreamProvider.StreamBufferSize;
return size > 0 ? size : DataServiceStreamProviderWrapper.DefaultBufferSize;
}
}
#endregion Public Properties
#region Private Properties
///
/// Asks the service for an IDataServiceStreamProvider implementation
///
private IDataServiceStreamProvider StreamProvider
{
get
{
if (this.streamProvider == null)
{
this.streamProvider = LoadStreamProvider(this.dataService);
Debug.Assert(this.streamProvider != null, "this.streamProvider != null");
}
return this.streamProvider;
}
}
#endregion Private Properties
#region Internal Methods
///
/// Take the given Media Link Entry uri, and construct the default Edit Media Uri.
///
/// Uri to the Media Link Entry.
/// Uri to the Media Resource.
internal static string GetStreamEditMediaUri(string mediaLinkEntryUri)
{
Debug.Assert(!string.IsNullOrEmpty(mediaLinkEntryUri), "!string.IsNullOrEmpty(mediaLinkEntryUri)");
string result = mediaLinkEntryUri;
if (!result.EndsWith(XmlConstants.UriValueSegment, StringComparison.Ordinal))
{
if (!result.EndsWith("/", StringComparison.Ordinal))
{
result += "/";
}
result += XmlConstants.UriValueSegment;
}
return result;
}
///
/// Asks the data service for a stream provider instance. Throw if none is implemented.
///
/// data service instance
/// stream provider instance
internal static IDataServiceStreamProvider LoadStreamProvider(IDataService dataService)
{
IDataServiceStreamProvider streamProvider = dataService.Provider.GetService(dataService);
if (streamProvider == null)
{
throw new DataServiceException(500, Strings.DataServiceStreamProviderWrapper_MustImplementIDataServiceStreamProviderToSupportStreaming);
}
return streamProvider;
}
///
/// This method is invoked by the data services framework to retrieve the default stream associated
/// with the Entity Type specified by the parameter.
/// Note that we set the response ETag in the host object before we return.
///
/// The stream returned should be the default stream associated with this entity.
/// A reference to the context for the current operation.
/// A valid stream the data service use to query / read a streamed BLOB which is associated with the .
internal Stream GetReadStream(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
string etagFromHeader;
bool? checkETagForEquality;
DataServiceStreamProviderWrapper.GetETagFromHeaders(operationContext, out etagFromHeader, out checkETagForEquality);
Debug.Assert(
string.IsNullOrEmpty(etagFromHeader) && !checkETagForEquality.HasValue || !string.IsNullOrEmpty(etagFromHeader) && checkETagForEquality.HasValue,
"etag and checkETagForEquality parameters must both be set or not set at the same time.");
Stream readStream = null;
try
{
readStream = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetReadStream", () => this.StreamProvider.GetReadStream(entity, etagFromHeader, checkETagForEquality, operationContext), operationContext);
}
catch (DataServiceException e)
{
if (e.StatusCode == (int)System.Net.HttpStatusCode.NotModified)
{
// For status code 304, we MUST set the etag value. Our Error handler will translate
// DataServiceException(304) to a normal response with status code 304 and an empty message-body.
#if DEBUG
WebUtil.WriteETagValueInResponseHeader(null, this.GetStreamETag(entity, operationContext), operationContext.Host);
#else
WebUtil.WriteETagValueInResponseHeader(this.GetStreamETag(entity, operationContext), operationContext.Host);
#endif
}
throw;
}
try
{
if (readStream == null || !readStream.CanRead)
{
throw new InvalidOperationException(Strings.DataService_InvalidStreamFromGetReadStream);
}
// GetStreamETag can throw and we need to catch and dispose the stream.
#if DEBUG
WebUtil.WriteETagValueInResponseHeader(null, this.GetStreamETag(entity, operationContext), operationContext.Host);
#else
WebUtil.WriteETagValueInResponseHeader(this.GetStreamETag(entity, operationContext), operationContext.Host);
#endif
}
catch
{
WebUtil.Dispose(readStream);
throw;
}
return readStream;
}
///
/// This method is invoked by the data services framework whenever an insert or update operation is
/// being processed for the stream associated with the Entity Type specified via the entity parameter.
///
/// The stream returned should be the default stream associated with this entity.
/// A reference to the context for the current operation.
/// A valid stream the data service use to write the contents of a BLOB which is associated with .
internal Stream GetWriteStream(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
string etag;
bool? checkETagForEquality;
DataServiceStreamProviderWrapper.GetETagFromHeaders(operationContext, out etag, out checkETagForEquality);
Debug.Assert(
string.IsNullOrEmpty(etag) && !checkETagForEquality.HasValue || !string.IsNullOrEmpty(etag) && checkETagForEquality.HasValue,
"etag and checkETagForEquality parameters must both be set or not set at the same time.");
Stream writeStream = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetWriteStream", () => this.StreamProvider.GetWriteStream(entity, etag, checkETagForEquality, operationContext), operationContext);
if (writeStream == null || !writeStream.CanWrite)
{
WebUtil.Dispose(writeStream);
throw new InvalidOperationException(Strings.DataService_InvalidStreamFromGetWriteStream);
}
return writeStream;
}
///
/// This method is invoked by the data services framework whenever an delete operation is being processed for the stream associated with
/// the Entity Type specified via the entity parameter.
///
/// The stream deleted should be the default stream associated with this entity.
/// A reference to the context for the current operation.
internal void DeleteStream(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.DeleteStream", () => { this.StreamProvider.DeleteStream(entity, operationContext); return true; }, operationContext);
}
///
/// This method is invoked by the data services framework to obtain the IANA content type (aka media type) of the stream associated
/// with the specified entity. This metadata is needed when constructing the payload for the Media Link Entry associated with the
/// stream (aka Media Resource) or setting the Content-Type HTTP response header.
///
/// The entity associated with the stream for which the content type is to be obtained
/// A reference to the context for the current operation.
/// Valid Content-Type string for the stream associated with the entity
internal string GetStreamContentType(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
string contentType = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetStreamContentType", () => this.StreamProvider.GetStreamContentType(entity, operationContext), operationContext);
if (string.IsNullOrEmpty(contentType))
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_GetStreamContentTypeReturnsEmptyOrNull);
}
return contentType;
}
///
/// This method is invoked by the data services framework to obtain the URI clients should use when making retrieve (ie. GET)
/// requests to the stream(ie. Media Resource). This metadata is needed when constructing the payload for the Media Link Entry
/// associated with the stream (aka Media Resource).
///
/// If IDataServiceStreamProvider.GetReadStreamUri returns a valid Uri, we return that as the Uri to the Media Resource.
/// Otherwise we take the given Media Link Entry uri, and construct the default Media Resource Uri.
///
/// The entity associated with the stream for which a “read stream” is to be obtained
/// A reference to the context for the current operation.
/// Uri to the Media Link Entry.
/// The URI clients should use when making retrieve (ie. GET) requests to the stream(ie. Media Resource).
internal Uri GetReadStreamUri(object entity, DataServiceOperationContext operationContext, string mediaLinkEntryUri)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
Uri readStreamUri = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetReadStreamUri", () => this.StreamProvider.GetReadStreamUri(entity, operationContext), operationContext);
if (readStreamUri != null)
{
if (!readStreamUri.IsAbsoluteUri)
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_GetReadStreamUriMustReturnAbsoluteUriOrNull);
}
else
{
return readStreamUri;
}
}
else
{
return new Uri(DataServiceStreamProviderWrapper.GetStreamEditMediaUri(mediaLinkEntryUri), UriKind.RelativeOrAbsolute);
}
}
///
/// This method is invoked by the data services framework to obtain the ETag of the stream associated with the entity specified.
/// This metadata is needed when constructing the payload for the Media Link Entry associated with the stream (aka Media Resource)
/// as well as to be used as the value of the ETag HTTP response header.
///
/// The entity associated with the stream for which an etag is to be obtained
/// A reference to the context for the current operation.
/// ETag of the stream associated with the entity specified
internal string GetStreamETag(object entity, DataServiceOperationContext operationContext)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
string etag = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.GetStreamETag", () => this.StreamProvider.GetStreamETag(entity, operationContext), operationContext);
if (!WebUtil.IsETagValueValid(etag, true))
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_GetStreamETagReturnedInvalidETagFormat);
}
return etag;
}
///
/// This method is invoked by the data services framework when a request is received to insert into an Entity Set with an associated
/// Entity Type hierarchy that has > 1 Entity Type and >= 1 Entity Type which is tagged as an MLE (ie. includes a stream).
///
/// Fully qualified name entity set name.
/// Data service instance.
///
/// Namespace qualified type name which represents the type the Astoria framework should instantiate to create the MLE associated
/// with the BLOB/MR being inserted.
///
internal ResourceType ResolveType(string entitySetName, IDataService service)
{
DataServiceOperationContext operationContext = service.OperationContext;
Debug.Assert(operationContext != null, "operationContext != null");
string resourceTypeName = InvokeApiCallAndValidateHeaders("IDataServiceStreamProvider.ResolveType", () => this.StreamProvider.ResolveType(entitySetName, operationContext), operationContext);
if (string.IsNullOrEmpty(resourceTypeName))
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_ResolveTypeMustReturnValidResourceTypeName);
}
ResourceType resourceType = service.Provider.TryResolveResourceType(resourceTypeName);
if (resourceType == null)
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_ResolveTypeMustReturnValidResourceTypeName);
}
return resourceType;
}
///
/// Gets the ETag, ReadStreamUri and ContentType of the stream
///
/// MLE instance
/// context of the current operation
/// Uri to the MLE
/// returns the etag for the stream
/// returns the read stream uri
/// returns the content type of the stream
internal void GetStreamDescription(object entity, DataServiceOperationContext operationContext, string mediaLinkEntryUri, out string etag, out Uri readStreamUri, out string contentType)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(operationContext != null, "operationContext != null");
// Call order is part of our contract, do not change it.
etag = this.GetStreamETag(entity, operationContext);
readStreamUri = this.GetReadStreamUri(entity, operationContext, mediaLinkEntryUri);
contentType = this.GetStreamContentType(entity, operationContext);
}
///
/// Dispose the stream provider instance
///
internal void DisposeProvider()
{
if (this.streamProvider != null)
{
WebUtil.Dispose(this.streamProvider);
this.streamProvider = null;
}
}
#endregion Public Methods
#region Private Methods
///
/// Get the ETag header value from the request headers.
///
/// A reference to the context for the current operation.
///
/// The etag value sent by the client (as the value of an If[-None-]Match header) as part of the HTTP request sent to the data service
/// This parameter will be null if no If[-None-]Match header was present
///
///
/// True if an value of the etag parameter was sent to the server as the value of an If-Match HTTP request header
/// False if an value of the etag parameter was sent to the server as the value of an If-None-Match HTTP request header
/// null if the HTTP request for the stream was not a conditional request
///
private static void GetETagFromHeaders(DataServiceOperationContext operationContext, out string etag, out bool? checkETagForEquality)
{
Debug.Assert(operationContext != null, "operationContext != null");
Debug.Assert(operationContext.Host != null, "operationContext.Host != null");
DataServiceHostWrapper host = operationContext.Host;
Debug.Assert(string.IsNullOrEmpty(host.RequestIfMatch) || string.IsNullOrEmpty(host.RequestIfNoneMatch), "IfMatch and IfNoneMatch should not be both set.");
if (string.IsNullOrEmpty(host.RequestIfMatch) && string.IsNullOrEmpty(host.RequestIfNoneMatch))
{
etag = null;
checkETagForEquality = null;
}
else if (!string.IsNullOrEmpty(host.RequestIfMatch))
{
etag = host.RequestIfMatch;
checkETagForEquality = true;
}
else
{
etag = host.RequestIfNoneMatch;
checkETagForEquality = false;
}
}
///
/// Invokes an API call and verifies the response Content-Type and ETag headers are not being modified by the API call.
///
/// Return type from the API call
/// API name
/// Delegate to be called
/// A reference to the context for the current operation.
/// Returns the result from the api call
private static T InvokeApiCallAndValidateHeaders(string methodName, Func apiCall, DataServiceOperationContext operationContext)
{
Debug.Assert(!string.IsNullOrEmpty(methodName), "!string.IsNullOrEmpty(methodName)");
Debug.Assert(operationContext != null, "operationContext != null");
Debug.Assert(apiCall != null, "apiCall != null");
string responseContentType = operationContext.Host.ResponseContentType;
string responseETag = operationContext.Host.ResponseETag;
T result = apiCall();
if (operationContext.Host.ResponseContentType != responseContentType || operationContext.Host.ResponseETag != responseETag)
{
throw new InvalidOperationException(Strings.DataServiceStreamProviderWrapper_MustNotSetContentTypeAndEtag(methodName));
}
return result;
}
#endregion Private Methods
}
}
// 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
- ForeignKeyFactory.cs
- Propagator.JoinPropagator.SubstitutingCloneVisitor.cs
- XslNumber.cs
- DecimalAnimationUsingKeyFrames.cs
- DmlSqlGenerator.cs
- XLinq.cs
- RSACryptoServiceProvider.cs
- PerfCounters.cs
- RegexCharClass.cs
- DesignTimeTemplateParser.cs
- SynchronizationContext.cs
- TranslateTransform3D.cs
- ApplicationDirectory.cs
- HttpResponseWrapper.cs
- ProviderBase.cs
- OLEDB_Util.cs
- Win32Native.cs
- BuildProviderAppliesToAttribute.cs
- GregorianCalendar.cs
- VoiceObjectToken.cs
- MessageDirection.cs
- MemberProjectionIndex.cs
- RetrieveVirtualItemEventArgs.cs
- BooleanConverter.cs
- InheritedPropertyChangedEventArgs.cs
- InputScope.cs
- ChainedAsyncResult.cs
- HwndSourceKeyboardInputSite.cs
- ThousandthOfEmRealDoubles.cs
- RightsManagementPermission.cs
- InstanceDescriptor.cs
- FullTextLine.cs
- EdmError.cs
- CustomErrorCollection.cs
- QueueTransferProtocol.cs
- GroupByQueryOperator.cs
- IconConverter.cs
- SynchronizationLockException.cs
- RegionInfo.cs
- HtmlInputReset.cs
- DictionarySurrogate.cs
- SynchronizedInputProviderWrapper.cs
- RootBrowserWindow.cs
- SecurityRuntime.cs
- ReadOnlyNameValueCollection.cs
- DesignerInterfaces.cs
- Unit.cs
- DataGridViewComboBoxEditingControl.cs
- DataBindingList.cs
- MarshalByRefObject.cs
- XmlException.cs
- BatchWriter.cs
- TdsParserSessionPool.cs
- DataControlReferenceCollection.cs
- SqlDuplicator.cs
- SamlAssertionKeyIdentifierClause.cs
- BezierSegment.cs
- HtmlInputControl.cs
- WebConfigurationManager.cs
- QilExpression.cs
- SchemaManager.cs
- Vector.cs
- SmtpNegotiateAuthenticationModule.cs
- InspectionWorker.cs
- DateTimeFormatInfoScanner.cs
- RuleConditionDialog.cs
- Crc32Helper.cs
- HtmlHead.cs
- WebReferenceOptions.cs
- WebReferencesBuildProvider.cs
- MethodCallConverter.cs
- AuthenticationConfig.cs
- Vertex.cs
- UInt64.cs
- Crypto.cs
- AnchoredBlock.cs
- BamlWriter.cs
- panel.cs
- ButtonChrome.cs
- XmlIgnoreAttribute.cs
- HtmlInputText.cs
- MobileResource.cs
- X509UI.cs
- WebRequest.cs
- FlowDocumentReaderAutomationPeer.cs
- DataTemplateSelector.cs
- Assembly.cs
- KeyValuePair.cs
- ExpressionVisitorHelpers.cs
- DispatchOperation.cs
- SchemaImporterExtensionElement.cs
- MessageAction.cs
- SQLMoneyStorage.cs
- ProtectedConfigurationSection.cs
- PropertyEntry.cs
- OdbcParameterCollection.cs
- KnownTypesProvider.cs
- FtpWebRequest.cs
- AsyncContentLoadedEventArgs.cs
- Italic.cs