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
- MaskedTextProvider.cs
- BaseWebProxyFinder.cs
- ProfilePropertySettings.cs
- WsiProfilesElement.cs
- GorillaCodec.cs
- DbDataReader.cs
- PageAsyncTask.cs
- RSAOAEPKeyExchangeDeformatter.cs
- BridgeDataReader.cs
- PostBackOptions.cs
- OdbcDataReader.cs
- CompositeClientFormatter.cs
- StatusBarDrawItemEvent.cs
- SqlConnection.cs
- WebPartZoneBaseDesigner.cs
- WindowsComboBox.cs
- EntityObject.cs
- SemaphoreFullException.cs
- RelationalExpressions.cs
- UserPersonalizationStateInfo.cs
- VBIdentifierTrimConverter.cs
- BlurBitmapEffect.cs
- InputLanguageCollection.cs
- TrustLevelCollection.cs
- SecurityTokenResolver.cs
- XmlUTF8TextReader.cs
- MappingException.cs
- FamilyMap.cs
- AsyncOperationManager.cs
- SchemaTypeEmitter.cs
- ConfigurationCollectionAttribute.cs
- util.cs
- ValidationPropertyAttribute.cs
- UrlAuthFailedErrorFormatter.cs
- CustomBindingElement.cs
- TableLayoutCellPaintEventArgs.cs
- CodeAccessPermission.cs
- MimeFormatExtensions.cs
- ViewStateException.cs
- ProviderMetadata.cs
- ListDesigner.cs
- DictionaryEditChange.cs
- ZipIOModeEnforcingStream.cs
- AnnotationAuthorChangedEventArgs.cs
- TraceXPathNavigator.cs
- RotateTransform3D.cs
- UniqueConstraint.cs
- SQLByteStorage.cs
- JsonReader.cs
- FieldDescriptor.cs
- ScriptControl.cs
- arclist.cs
- AddingNewEventArgs.cs
- XamlSerializerUtil.cs
- WorkerRequest.cs
- GetPageNumberCompletedEventArgs.cs
- XPathSingletonIterator.cs
- InvalidCommandTreeException.cs
- StreamUpdate.cs
- BindingContext.cs
- Method.cs
- EventProviderWriter.cs
- ErrorFormatter.cs
- altserialization.cs
- BadImageFormatException.cs
- Sentence.cs
- SoapAttributeOverrides.cs
- FontInfo.cs
- RSAOAEPKeyExchangeFormatter.cs
- HtmlInputControl.cs
- AesManaged.cs
- WorkerRequest.cs
- XmlSchemaComplexContentExtension.cs
- Message.cs
- FillErrorEventArgs.cs
- Msec.cs
- EventManager.cs
- CodeExpressionStatement.cs
- AuthenticationModuleElementCollection.cs
- AuthorizationSection.cs
- Token.cs
- OutputCacheProfileCollection.cs
- IdnElement.cs
- ExcCanonicalXml.cs
- UrlParameterWriter.cs
- ExpressionQuoter.cs
- NativeMethodsCLR.cs
- IProvider.cs
- XmlAttributeCollection.cs
- HttpMethodAttribute.cs
- MimeMapping.cs
- TypeBrowser.xaml.cs
- SignatureResourcePool.cs
- SafeNativeMethods.cs
- CodeArrayIndexerExpression.cs
- DeferredElementTreeState.cs
- X509SecurityTokenProvider.cs
- StreamInfo.cs
- SchemaCollectionPreprocessor.cs
- control.ime.cs