DataService.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / DataService.cs / 1458001 / DataService.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a base class for DataWeb services.
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services
{
    #region Namespaces. 

    using System; 
    using System.Collections; 
    using System.Collections.Generic;
    using System.Data.Objects; 
    using System.Data.Services.Caching;
    using System.Data.Services.Providers;
    using System.Data.Services.Serializers;
    using System.Data.Services.Common; 
    using System.Diagnostics;
    using System.IO; 
    using System.Linq; 
    using System.ServiceModel;
    using System.ServiceModel.Activation; 
    using System.ServiceModel.Channels;
#if ASTORIA_FF_CALLBACKS
    using System.ServiceModel.Syndication;
#endif 
    using System.Text;
 
    #endregion Namespaces. 

    ///  
    /// Represents a strongly typed service that can process data-oriented
    /// resource requests.
    /// 
    /// The type of the store to provide resources. 
    /// 
    ///  will typically be a subtype of 
    ///  or another class that provides  
    /// properties.
    ///  
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class DataService : IRequestHandler, IDataService
    { 
        #region Private fields.
 
        /// A delegate used to create an instance of the data context. 
        private static Func cachedConstructor;
 
        /// Service configuration information.
        private DataServiceConfiguration configuration;

        /// Data provider for this data service. 
        private DataServiceProviderWrapper provider;
 
        /// IUpdatable interface for this datasource's provider 
        private UpdatableWrapper updatable;
 
        /// Custom paging provider interface exposed by the service.
        private DataServicePagingProviderWrapper pagingProvider;

        /// Context for the current operation. 
        private DataServiceOperationContext operationContext;
 
        /// Reference to IDataServiceStreamProvider interface. 
        private DataServiceStreamProviderWrapper streamProvider;
 
        /// Events for the data service processing pipeline.
        private DataServiceProcessingPipeline processingPipeline = new DataServiceProcessingPipeline();

#if DEBUG 
#pragma warning disable 0169
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823")] 
        private Action requestQueryableConstructed; 
#pragma warning restore 0169
#endif 

        #endregion Private fields.

        #region Properties. 

        /// Events for the data service processing pipeline. 
        public DataServiceProcessingPipeline ProcessingPipeline 
        {
            [DebuggerStepThrough] 
            get { return this.processingPipeline; }
        }

        /// Service configuration information. 
        DataServiceConfiguration IDataService.Configuration
        { 
            [DebuggerStepThrough] 
            get { return this.configuration; }
        } 

        /// Data provider for this data service
        DataServiceProviderWrapper IDataService.Provider
        { 
            [DebuggerStepThrough]
            get 
            { 
                return this.provider;
            } 
        }

        /// Paging provider for this data service.
        DataServicePagingProviderWrapper IDataService.PagingProvider 
        {
            [DebuggerStepThrough] 
            get 
            {
                Debug.Assert(this.provider != null, "this.provider != null"); 
                if (this.pagingProvider == null)
                {
                    this.pagingProvider = new DataServicePagingProviderWrapper(this);
                } 

                return this.pagingProvider; 
            } 
        }
 
        /// Returns the instance of data service.
        object IDataService.Instance
        {
            [DebuggerStepThrough] 
            get { return this; }
        } 
 
        /// Cached request headers.
        DataServiceOperationContext IDataService.OperationContext 
        {
            [DebuggerStepThrough]
            get { return this.operationContext; }
        } 

        /// Processing pipeline events 
        DataServiceProcessingPipeline IDataService.ProcessingPipeline 
        {
            [DebuggerStepThrough] 
            get { return this.processingPipeline; }
        }

        /// IUpdatable interface for this provider 
        UpdatableWrapper IDataService.Updatable
        { 
            [DebuggerStepThrough] 
            get
            { 
                Debug.Assert(this.provider != null, "this.provider != null");
                return this.updatable;
            }
        } 

        /// Reference to IDataServiceStreamProvider interface. 
        DataServiceStreamProviderWrapper IDataService.StreamProvider 
        {
            [DebuggerStepThrough] 
            get
            {
                Debug.Assert(this.provider != null, "this.provider != null");
                return this.streamProvider ?? (this.streamProvider = new DataServiceStreamProviderWrapper(this)); 
            }
        } 
 
        /// The data source used in the current request processing.
        protected T CurrentDataSource 
        {
            get { return (T)this.provider.CurrentDataSource; }
        }
 
        #endregion Properties.
 
        #region Public / interface methods. 

        ///  
        /// This method is called during query processing to validate and customize
        /// paths for the $expand options are applied by the provider.
        /// 
        /// Query which will be composed. 
        /// Collection of segment paths to be expanded.
        void IDataService.InternalApplyingExpansions(IQueryable queryable, ICollection expandPaths) 
        { 
            Debug.Assert(queryable != null, "queryable != null");
            Debug.Assert(expandPaths != null, "expandPaths != null"); 
            Debug.Assert(this.configuration != null, "this.configuration != null");

            // Check the expand depth and count.
            int actualExpandDepth = 0; 
            int actualExpandCount = 0;
            foreach (ExpandSegmentCollection collection in expandPaths) 
            { 
                int segmentDepth = collection.Count;
                if (segmentDepth > actualExpandDepth) 
                {
                    actualExpandDepth = segmentDepth;
                }
 
                actualExpandCount += segmentDepth;
            } 
 
            if (this.configuration.MaxExpandDepth < actualExpandDepth)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ExpandDepthExceeded(actualExpandDepth, this.configuration.MaxExpandDepth));
            }

            if (this.configuration.MaxExpandCount < actualExpandCount) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ExpandCountExceeded(actualExpandCount, this.configuration.MaxExpandCount)); 
            } 
        }
 
        /// Processes a catchable exception.
        /// The arguments describing how to handle the exception.
        void IDataService.InternalHandleException(HandleExceptionArgs args)
        { 
            Debug.Assert(args != null, "args != null");
            try 
            { 
                this.HandleException(args);
            } 
            catch (Exception handlingException)
            {
#if DEBUG
                // We've hit an error, some of the processing pipeline events will not get fired. Setting this flag to skip validation. 
                this.ProcessingPipeline.SkipDebugAssert = true;
#endif 
                if (!WebUtil.IsCatchableExceptionType(handlingException)) 
                {
                    throw; 
                }

                args.Exception = handlingException;
            } 
        }
 
#if ASTORIA_FF_CALLBACKS 
        /// 
        /// Invoked once feed has been written to override the feed elements 
        /// 
        /// Feed being written
        void IDataService.InternalOnWriteFeed(SyndicationFeed feed)
        { 
            this.OnWriteFeed(feed);
        } 
 
        /// 
        /// Invoked once an element has been written to override the element 
        /// 
        /// Item that has been written
        /// Object with content for the 
        void IDataService.InternalOnWriteItem(SyndicationItem item, object obj) 
        {
            this.OnWriteItem(item, obj); 
        } 
#endif
        ///  
        /// Returns the segmentInfo of the resource referred by the given content Id;
        /// 
        /// content id for a operation in the batch request.
        /// segmentInfo for the resource referred by the given content id. 
        SegmentInfo IDataService.GetSegmentForContentId(string contentId)
        { 
            return null; 
        }
 
        /// 
        /// Get the resource referred by the segment in the request with the given index
        /// 
        /// description about the request url. 
        /// index of the segment that refers to the resource that needs to be returned.
        /// typename of the resource. 
        /// the resource as returned by the provider. 
        object IDataService.GetResource(RequestDescription description, int segmentIndex, string typeFullName)
        { 
            Debug.Assert(description.SegmentInfos[segmentIndex].RequestEnumerable != null, "requestDescription.SegmentInfos[segmentIndex].RequestEnumerable != null");
            return Deserializer.GetResource(description.SegmentInfos[segmentIndex], typeFullName, ((IDataService)this), false /*checkForNull*/);
        }
 
        /// Disposes the data source of the current  if necessary.
        ///  
        /// Because the provider has affinity with a specific data source 
        /// (which is created and set by the DataService), we set
        /// the provider to null so we remember to re-create it if the 
        /// service gets reused for a different request.
        /// 
        void IDataService.DisposeDataSource()
        { 
#if DEBUG
            this.processingPipeline.AssertDebugStateAtDispose(); 
            this.processingPipeline.HasDisposedProviderInterfaces = true; 
#endif
            if (this.updatable != null) 
            {
                this.updatable.DisposeProvider();
                this.updatable = null;
            } 

            if (this.streamProvider != null) 
            { 
                this.streamProvider.DisposeProvider();
                this.streamProvider = null; 
            }

            if (this.pagingProvider != null)
            { 
                this.pagingProvider.DisposeProvider();
                this.pagingProvider = null; 
            } 

            if (this.provider != null) 
            {
                this.provider.DisposeDataSource();
                this.provider = null;
            } 
        }
 
        ///  
        /// This method is called before a request is processed.
        ///  
        /// Information about the request that is going to be processed.
        void IDataService.InternalOnStartProcessingRequest(ProcessRequestArgs args)
        {
#if DEBUG 
            this.processingPipeline.AssertDebugStateAtOnStartProcessingRequest();
            this.processingPipeline.OnStartProcessingRequestInvokeCount++; 
#endif 
            this.OnStartProcessingRequest(args);
        } 

        /// Attaches the specified host to this service.
        /// Host for service to interact with.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "host", Justification = "Makes 1:1 argument-to-field correspondence obvious.")] 
        public void AttachHost(IDataServiceHost host)
        { 
            WebUtil.CheckArgumentNull(host, "host"); 
            this.operationContext = new DataServiceOperationContext(host);
        } 

        /// Processes the specified .
        ///  with message body to process.
        /// The response . 
        public Message ProcessRequestForMessage(Stream messageBody)
        { 
            WebUtil.CheckArgumentNull(messageBody, "messageBody"); 

            HttpContextServiceHost httpHost = new HttpContextServiceHost(messageBody); 
            this.AttachHost(httpHost);

            bool shouldDispose = true;
            try 
            {
                Action writer = this.HandleRequest(); 
                Debug.Assert(writer != null, "writer != null"); 
                Message result = CreateMessage(MessageVersion.None, "", ((IDataServiceHost)httpHost).ResponseContentType, writer, this);
 
                // If SuppressEntityBody is false, WCF will call DelegateBodyWriter.OnWriteBodyContent(), which
                // will dispose the data source and stream provider.  Otherwise we need to dispose them in the
                // finally block below.
                if (!System.ServiceModel.Web.WebOperationContext.Current.OutgoingResponse.SuppressEntityBody) 
                {
                    shouldDispose = false; 
                } 

                return result; 
            }
#if DEBUG
            catch
            { 
                this.processingPipeline.SkipDebugAssert = true;
                throw; 
            } 
#endif
            finally 
            {
                if (shouldDispose)
                {
                    ((IDataService)this).DisposeDataSource(); 
                }
            } 
        } 

        /// Provides a host-agnostic entry point for request processing. 
        public void ProcessRequest()
        {
            if (this.operationContext == null)
            { 
                throw new InvalidOperationException(Strings.DataService_HostNotAttached);
            } 
 
            try
            { 
                Action writer = this.HandleRequest();
                if (writer != null)
                {
                    writer(this.operationContext.Host.ResponseStream); 
                }
            } 
#if DEBUG 
            catch
            { 
                this.processingPipeline.SkipDebugAssert = true;
                throw;
            }
#endif 
            finally
            { 
                ((IDataService)this).DisposeDataSource(); 
#if DEBUG
                // Need to reset the states since the caller can reuse the same service instance. 
                this.processingPipeline.ResetDebugState();
#endif
            }
        } 

        #endregion Public / interface methods. 
 
        #region Protected methods.
 
        /// Initializes a new data source instance.
        /// A new data source instance.
        /// 
        /// The default implementation uses a constructor with no parameters 
        /// to create a new instance.
        /// 
        /// The instance will only be used for the duration of a single 
        /// request, and will be disposed after the request has been
        /// handled. 
        /// 
        protected virtual T CreateDataSource()
        {
            if (cachedConstructor == null) 
            {
                Type dataContextType = typeof(T); 
                if (dataContextType.IsAbstract) 
                {
                    throw new InvalidOperationException( 
                        Strings.DataService_ContextTypeIsAbstract(dataContextType, this.GetType()));
                }

                cachedConstructor = (Func)WebUtil.CreateNewInstanceConstructor(dataContextType, null, dataContextType); 
            }
 
            return cachedConstructor(); 
        }
 
        /// Handles an exception thrown while processing a request.
        /// Arguments to the exception.
        protected virtual void HandleException(HandleExceptionArgs args)
        { 
            WebUtil.CheckArgumentNull(args, "arg");
            Debug.Assert(args.Exception != null, "args.Exception != null -- .ctor should have checked"); 
#if DEBUG 
            this.processingPipeline.SkipDebugAssert = true;
#endif 
        }

        /// 
        /// This method is called before processing each request. For batch requests 
        /// it is called once for the top batch request and once for each operation
        /// in the batch. 
        ///  
        /// args containing information about the request.
        protected virtual void OnStartProcessingRequest(ProcessRequestArgs args) 
        {
            // Do nothing. Application writers can override this and look
            // at the request args and do some processing.
        } 

#if ASTORIA_FF_CALLBACKS 
        ///  
        /// Invoked once feed has been written to override the feed elements
        ///  
        /// Feed being written
        protected virtual void OnWriteFeed(SyndicationFeed feed)
        {
        } 

        ///  
        /// Invoked once an element has been written to override the element 
        /// 
        /// Item that has been written 
        /// Object with content for the 
        protected virtual void OnWriteItem(SyndicationItem item, object obj)
        {
        } 
#endif
        #endregion Protected methods. 
 
        #region Private methods.
 
        /// 
        /// Checks that if etag values are specified in the header, they must be valid.
        /// 
        /// header values. 
        /// request description.
        private static void CheckETagValues(DataServiceHostWrapper host, RequestDescription description) 
        { 
            Debug.Assert(host != null, "host != null");
 
            // Media Resource ETags can be strong
            bool allowStrongEtag = description.TargetKind == RequestTargetKind.MediaResource;

            if (!WebUtil.IsETagValueValid(host.RequestIfMatch, allowStrongEtag)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagValueNotValid(host.RequestIfMatch)); 
            } 

            if (!WebUtil.IsETagValueValid(host.RequestIfNoneMatch, allowStrongEtag)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagValueNotValid(host.RequestIfNoneMatch));
            }
        } 

        ///  
        /// Creates a  that invokes the specified 
        ///  callback to write its body.
        ///  
        /// Version for message.
        /// Action for message.
        /// MIME content type for body.
        /// Callback. 
        /// Service with context to dispose once the response has been written.
        /// A new . 
        private static Message CreateMessage(MessageVersion version, string action, string contentType, Action writer, IDataService service) 
        {
            Debug.Assert(version != null, "version != null"); 
            Debug.Assert(writer != null, "writer != null");
            Debug.Assert(service != null, "service != null");

            DelegateBodyWriter bodyWriter = new DelegateBodyWriter(writer, service); 

            Message message = Message.CreateMessage(version, action, bodyWriter); 
            message.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw)); 

            HttpResponseMessageProperty response = new HttpResponseMessageProperty(); 
            response.Headers[System.Net.HttpResponseHeader.ContentType] = contentType;
            message.Properties.Add(HttpResponseMessageProperty.Name, response);

            return message; 
        }
 
        ///  
        /// Creates a new data service configuration instance
        ///  
        /// data service type
        /// provider instance
        /// data service configuration instance
        private static DataServiceConfiguration CreateConfiguration(Type dataServiceType, IDataServiceMetadataProvider provider) 
        {
            Debug.Assert(dataServiceType != null, "dataServiceType != null"); 
            Debug.Assert(provider != null, "provider != null"); 

            DataServiceConfiguration configuration = new DataServiceConfiguration(provider); 
            configuration.Initialize(dataServiceType);

            if (!(provider is BaseServiceProvider) && configuration.GetKnownTypes().Any())
            { 
                throw new InvalidOperationException(Strings.DataService_RegisterKnownTypeNotAllowedForIDSP);
            } 
 
            configuration.Seal();
            return configuration; 
        }

        /// 
        /// Gets the appropriate encoding specified by the request, taking 
        /// the format into consideration.
        ///  
        /// Content format for response. 
        /// Accept-Charset header as specified in request.
        /// The requested encoding, possibly null. 
        private static Encoding GetRequestAcceptEncoding(ContentFormat responseFormat, string acceptCharset)
        {
            if (responseFormat == ContentFormat.Binary)
            { 
                return null;
            } 
            else 
            {
                return HttpProcessUtility.EncodingFromAcceptCharset(acceptCharset); 
            }
        }

        ///  
        /// Selects a response format for the host's request and sets the
        /// appropriate response header. 
        ///  
        /// Host with request.
        /// An comma-delimited list of client-supported MIME accept types. 
        /// Whether the target is an entity.
        /// The selected response format.
        private static ContentFormat SelectResponseFormat(DataServiceHostWrapper host, string acceptTypesText, bool entityTarget)
        { 
            Debug.Assert(host != null, "host != null");
 
            string[] availableTypes; 
            if (entityTarget)
            { 
                availableTypes = new string[]
                {
                    XmlConstants.MimeApplicationAtom,
                    XmlConstants.MimeApplicationJson 
                };
            } 
            else 
            {
                availableTypes = new string[] 
                {
                    XmlConstants.MimeApplicationXml,
                    XmlConstants.MimeTextXml,
                    XmlConstants.MimeApplicationJson 
                };
            } 
 
            string mime = HttpProcessUtility.SelectMimeType(acceptTypesText, availableTypes);
            if (mime == null) 
            {
                return ContentFormat.Unsupported;
            }
            else 
            {
                host.ResponseContentType = mime; 
                return GetContentFormat(mime); 
            }
        } 

        /// Validate the given request.
        /// Context for current operation.
        private static void ValidateRequest(DataServiceOperationContext operationContext) 
        {
            if (!String.IsNullOrEmpty(operationContext.Host.RequestIfMatch) && !String.IsNullOrEmpty(operationContext.Host.RequestIfNoneMatch)) 
            { 
                throw DataServiceException.CreateBadRequestError(Strings.DataService_BothIfMatchAndIfNoneMatchHeaderSpecified);
            } 
        }

        /// 
        /// Raises the response version header if necessary for the $metadata path. 
        /// WARNING!!! This property can only be called for the $metadata path because it enumerates through all resource types.
        /// Calling it from outside of the $metadata path would break our IDSP contract. 
        ///  
        /// description about the request uri
        /// data service to which the request was made 
        private static void RaiseResponseVersionForMetadata(RequestDescription description, IDataService dataService)
        {
            Debug.Assert(description.TargetKind == RequestTargetKind.Metadata, "This method can only be called from the $metadata path because it enumerates through all resource types.");
 
            if (dataService.Provider.IsV1Provider)
            { 
                if (!dataService.Provider.GetEpmCompatiblityForV1Provider()) 
                {
                    description.RaiseResponseVersion(2, 0); 
                }
            }
            else
            { 
                foreach (ResourceType rt in dataService.Provider.Types)
                { 
                    if (!rt.EpmIsV1Compatible) 
                    {
                        description.RaiseResponseVersion(2, 0); 
                        break;
                    }
                }
            } 
        }
 
        ///  
        /// Processes the incoming request, without writing anything to the response body.
        ///  
        /// description about the request uri
        /// data service to which the request was made.
        /// 
        /// A delegate to be called to write the body; null if no body should be written out. 
        /// 
        private static RequestDescription ProcessIncomingRequest( 
            RequestDescription description, 
            IDataService dataService)
        { 
            Debug.Assert(description != null, "description != null");
            Debug.Assert(dataService.OperationContext.Host != null, "dataService.OperationContext.Host != null");

            DataServiceHostWrapper host = dataService.OperationContext.Host; 

            // Make a decision about metadata response version 
            if (description.TargetKind == RequestTargetKind.Metadata) 
            {
                RaiseResponseVersionForMetadata(description, dataService); 
            }

            dataService.Configuration.ValidateMaxProtocolVersion(description);
 
            WebUtil.CheckVersion(dataService, description);
            CheckETagValues(host, description); 
 
            ResourceSetWrapper lastSegmentContainer = description.LastSegmentInfo.TargetContainer;
            if (host.AstoriaHttpVerb == AstoriaVerbs.GET) 
            {
                // This if expression was missing from V1.0, but is a breaking change to add it
                // without also checking for the new OverrideEntitySetRights
                if (description.LastSegmentInfo.Operation != null && 
                    (0 != (dataService.Configuration.GetServiceOperationRights(description.LastSegmentInfo.Operation.ServiceOperation) & ServiceOperationRights.OverrideEntitySetRights)))
                { 
                    DataServiceConfiguration.CheckServiceRights(description.LastSegmentInfo.Operation, description.IsSingleResult); 
                }
                else 
                {
                    // For $count, the rights is already checked in the RequestUriProcessor and hence we don't need to check here.
                    // Also, checking for ReadSingle right is wrong, since we need to only check for ReadMultiple rights.
                    if (lastSegmentContainer != null && description.LastSegmentInfo.Identifier != XmlConstants.UriCountSegment) 
                    {
                        DataServiceConfiguration.CheckResourceRightsForRead(lastSegmentContainer, description.IsSingleResult); 
                    } 
                }
            } 
            else if (description.TargetKind == RequestTargetKind.ServiceDirectory)
            {
                throw DataServiceException.CreateMethodNotAllowed(
                    Strings.DataService_OnlyGetOperationSupportedOnServiceUrl, 
                    XmlConstants.HttpMethodGet);
            } 
 
            int statusCode = 200;
            bool shouldWriteBody = true; 
            RequestDescription newDescription = description;
            if (description.TargetSource != RequestTargetSource.ServiceOperation)
            {
                if (host.AstoriaHttpVerb == AstoriaVerbs.POST) 
                {
                    newDescription = HandlePostOperation(description, dataService); 
                    if (description.LinkUri) 
                    {
                        statusCode = 204;   // 204 - No Content 
                        shouldWriteBody = false;
                    }
                    else
                    { 
                        statusCode = 201;   // 201 - Created.
                    } 
                } 
                else if (host.AstoriaHttpVerb == AstoriaVerbs.PUT ||
                         host.AstoriaHttpVerb == AstoriaVerbs.MERGE) 
                {
                    if (lastSegmentContainer != null && !description.LinkUri)
                    {
                        if (host.AstoriaHttpVerb == AstoriaVerbs.PUT) 
                        {
                            DataServiceConfiguration.CheckResourceRights(lastSegmentContainer, EntitySetRights.WriteReplace); 
                        } 
                        else
                        { 
                            DataServiceConfiguration.CheckResourceRights(lastSegmentContainer, EntitySetRights.WriteMerge);
                        }
                    }
 
                    // For PUT, the body itself shouldn't be written, but the etag should (unless it's just a link).
                    shouldWriteBody = !description.LinkUri; 
 
                    newDescription = HandlePutOperation(description, dataService);
                    Debug.Assert(description.ResponseVersion == RequestDescription.DataServiceDefaultResponseVersion, "description.ResponseVersion == RequestDescription.DataServiceDefaultResponseVersion"); 

                    statusCode = 204;   // 204 - No Content
                }
                else if (host.AstoriaHttpVerb == AstoriaVerbs.DELETE) 
                {
                    if (lastSegmentContainer != null && !description.LinkUri) 
                    { 
                        DataServiceConfiguration.CheckResourceRights(lastSegmentContainer, EntitySetRights.WriteDelete);
                    } 

                    HandleDeleteOperation(description, dataService);
                    Debug.Assert(description.RequireMinimumVersion == new Version(1, 0), "description.RequireMinimumVersion == new Version(1, 0)");
                    Debug.Assert(description.ResponseVersion == RequestDescription.DataServiceDefaultResponseVersion, "description.ResponseVersion == RequestDescription.DataServiceDefaultResponseVersion"); 

                    statusCode = 204;   // 204 - No Content 
                    shouldWriteBody = false; 
                }
            } 
            else if (description.TargetKind == RequestTargetKind.VoidServiceOperation)
            {
                statusCode = 204; // No Content
                shouldWriteBody = false; 
            }
 
            // Set the caching policy appropriately - for the time being, we disable caching. 
            host.ResponseCacheControl = XmlConstants.HttpCacheControlNoCache;
 
            // Always set the version when a payload will be returned, in case other
            // headers include links, which may need to be interpreted under version-specific rules.
            Debug.Assert(description.ResponseVersion == newDescription.ResponseVersion, "description.ResponseVersion == newDescription.ResponseVersion");
            host.ResponseVersion = newDescription.ResponseVersion.ToString() + ";"; 

            host.ResponseStatusCode = statusCode; 
 
            if (shouldWriteBody)
            { 
                // return the description, only if response or something in the response header needs to be written
                // for e.g. in PUT operations, we need to write etag to the response header, and
                // we can compute the new etag only after we have called save changes.
                return newDescription; 
            }
            else 
            { 
                return null;
            } 
        }

        /// Serializes the results for a request into the body of a response message.
        /// Description of the data requested. 
        /// data service to which the request was made.
        /// A delegate that can serialize the body into an IEnumerable. 
        private static Action SerializeResponseBody(RequestDescription description, IDataService dataService) 
        {
            Debug.Assert(dataService.Provider != null, "dataService.Provider != null"); 
            Debug.Assert(dataService.OperationContext.Host != null, "dataService.OperationContext.Host != null");

            DataServiceHostWrapper host = dataService.OperationContext.Host;
 
            // Handle internal system resources.
            Action result = HandleInternalResources(description, dataService); 
            if (result != null) 
            {
                return result; 
            }

            // ETags are not supported if there are more than one resource expected in the response.
            if (!RequestDescription.IsETagHeaderAllowed(description)) 
            {
                if (!String.IsNullOrEmpty(host.RequestIfMatch) || !String.IsNullOrEmpty(host.RequestIfNoneMatch)) 
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagCannotBeSpecified(host.AbsoluteRequestUri));
                } 
            }

            if (host.AstoriaHttpVerb == AstoriaVerbs.PUT ||
                host.AstoriaHttpVerb == AstoriaVerbs.MERGE) 
            {
                ResourceSetWrapper container; 
                object actualEntity = GetContainerAndActualEntityInstance(dataService, description, out container); 

                // We should only write etag in the response, if the type has one or more etag properties defined. 
                // WriteETagValueInResponseHeader checks for null etag value (which means that no etag properties are defined)
                // that before calling the host.
                string etag;
                if (description.TargetKind == RequestTargetKind.MediaResource) 
                {
                    etag = dataService.StreamProvider.GetStreamETag(actualEntity, dataService.OperationContext); 
                } 
                else
                { 
                    etag = WebUtil.GetETagValue(dataService, actualEntity, container);
                }

#if DEBUG 
                WebUtil.WriteETagValueInResponseHeader(description, etag, host);
#else 
                WebUtil.WriteETagValueInResponseHeader(etag, host); 
#endif
                return WebUtil.GetEmptyStreamWriter(); 
            }

            // Pick the content format to be used to serialize the body.
            Debug.Assert(description.RequestEnumerable != null, "description.RequestEnumerable != null"); 
            ContentFormat responseFormat = SelectResponseFormatForType(
                description.LinkUri ? RequestTargetKind.Link : description.TargetKind, 
                description.TargetResourceType, 
                host.RequestAccept,
                description.MimeType, 
                dataService);

            // This is the code path for service operations and GET requests returning multiple results
            if (description.TargetSource == RequestTargetSource.ServiceOperation || 
                description.TargetSource == RequestTargetSource.None ||
                !description.IsSingleResult) 
            { 
                // For service operations returning single result, etag checks must be performed by the service operation itself.
                Debug.Assert( 
                    (String.IsNullOrEmpty(host.RequestIfMatch) && String.IsNullOrEmpty(host.RequestIfNoneMatch)) || description.TargetSource == RequestTargetSource.ServiceOperation,
                    "No etag can be specified for collection or it must be a service operation");

                Encoding encoding = GetRequestAcceptEncoding(responseFormat, host.RequestAcceptCharSet); 
                IEnumerator queryResults = WebUtil.GetRequestEnumerator(description.RequestEnumerable);
 
                try 
                {
                    bool hasMoved = queryResults.MoveNext(); 

                    if (description.IsSingleResult)
                    {
                        if (!hasMoved || queryResults.Current == null) 
                        {
                            throw DataServiceException.CreateResourceNotFound(description.LastSegmentInfo.Identifier); 
                        } 
                    }
 
                    // If we had to wait until we got a value to determine the valid contents, try that now.
                    if (responseFormat == ContentFormat.Unknown)
                    {
                        responseFormat = ResolveUnknownFormat(description, queryResults.Current, dataService); 
                    }
 
                    Debug.Assert(responseFormat != ContentFormat.Unknown, "responseFormat != ContentFormat.Unknown"); 
                    host.ResponseContentType = HttpProcessUtility.BuildContentType(host.ResponseContentType, encoding);
                    return new ResponseBodyWriter(encoding, hasMoved, dataService, queryResults, description, responseFormat).Write; 
                }
                catch
                {
                    WebUtil.Dispose(queryResults); 
                    throw;
                } 
            } 
            else
            { 
                return CompareETagAndWriteResponse(description, responseFormat, dataService);
            }
        }
 
        /// Selects the correct content format for a given resource type.
        /// Target resource to return. 
        /// resource type. 
        /// Accept header value.
        /// Required MIME type. 
        /// Data service.
        /// 
        /// The content format for the resource; Unknown if it cannot be determined statically.
        ///  
        private static ContentFormat SelectResponseFormatForType(
            RequestTargetKind targetKind, 
            ResourceType resourceType, 
            string acceptTypesText,
            string mimeType, 
            IDataService service)
        {
            ContentFormat responseFormat;
            if (targetKind == RequestTargetKind.PrimitiveValue) 
            {
                responseFormat = SelectPrimitiveContentType(resourceType, acceptTypesText, mimeType, service.OperationContext.Host); 
            } 
            else if (targetKind == RequestTargetKind.MediaResource)
            { 
                // We need the MLE instance to get the response format for the MediaResource.
                // We will resolve the response format in ResolveUnknownFormat() where we have the MLE instance.
                responseFormat = ContentFormat.Unknown;
            } 
            else if (targetKind != RequestTargetKind.OpenPropertyValue)
            { 
                bool entityTarget = targetKind == RequestTargetKind.Resource; 
                responseFormat = SelectResponseFormat(service.OperationContext.Host, acceptTypesText, entityTarget);
                if (responseFormat == ContentFormat.Unsupported) 
                {
                    throw new DataServiceException(415, Strings.DataServiceException_UnsupportedMediaType);
                }
            } 
            else
            { 
                // We cannot negotiate a format until we know what the value is for the object. 
                responseFormat = ContentFormat.Unknown;
            } 

            return responseFormat;
        }
 
        /// Selects the correct content format for a primitive type.
        /// resource type. 
        /// Accept header value. 
        /// Required MIME type, possibly null.
        /// Host implementation for this data service. 
        /// The content format for the resource.
        private static ContentFormat SelectPrimitiveContentType(ResourceType targetResourceType, string acceptTypesText, string requiredContentType, DataServiceHostWrapper host)
        {
            // Debug.Assert( 
            //    targetResourceType != null &&
            //    targetResourceType.ResourceTypeKind == ResourceTypeKind.Primitive, 
            //    "targetElementType != null && targetResourceType.ResourceTypeKind == ResourceTypeKind.Primitive"); 
            string contentType;
            ContentFormat responseFormat = WebUtil.GetResponseFormatForPrimitiveValue(targetResourceType, out contentType); 
            requiredContentType = requiredContentType ?? contentType;
            host.ResponseContentType = HttpProcessUtility.SelectRequiredMimeType(
                acceptTypesText,        // acceptTypesText
                new string[] { requiredContentType },    // exactContentType 
                requiredContentType);   // inexactContentType
            return responseFormat; 
        } 

        /// Selects the correct content format for a media resource. 
        /// The media link entry.
        /// Accept header value.
        /// Data service instance.
        /// The content format for the resource. 
        private static ContentFormat SelectMediaResourceContentType(object mediaLinkEntry, string acceptTypesText, IDataService service)
        { 
            Debug.Assert(mediaLinkEntry != null, "mediaLinkEntry != null"); 
            Debug.Assert(service != null, "service != null");
 
            string contentType = service.StreamProvider.GetStreamContentType(mediaLinkEntry, service.OperationContext);

            service.OperationContext.Host.ResponseContentType = HttpProcessUtility.SelectRequiredMimeType(
                acceptTypesText,                 // acceptTypesText 
                new string[] { contentType },    // exactContentType
                contentType);                    // inexactContentType 
 
            return ContentFormat.Binary;
        } 

        /// Handles POST requests.
        /// description about the target request
        /// data service to which the request was made. 
        /// a new request description object, containing information about the response payload
        private static RequestDescription HandlePostOperation(RequestDescription description, IDataService dataService) 
        { 
            Debug.Assert(
                description.TargetSource != RequestTargetSource.ServiceOperation, 
                "TargetSource != ServiceOperation -- should have been handled in request URI processing");

            DataServiceHostWrapper host = dataService.OperationContext.Host;
            if (!String.IsNullOrEmpty(host.RequestIfMatch) || !String.IsNullOrEmpty(host.RequestIfNoneMatch)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagSpecifiedForPost); 
            } 

            if (description.IsSingleResult) 
            {
                throw DataServiceException.CreateMethodNotAllowed(
                    Strings.BadRequest_InvalidUriForPostOperation(host.AbsoluteRequestUri),
                    DataServiceConfiguration.GetAllowedMethods(dataService.Configuration, description)); 
            }
 
            Debug.Assert( 
                description.TargetSource == RequestTargetSource.EntitySet ||
                description.Property.Kind == ResourcePropertyKind.ResourceSetReference, 
                "Only ways to have collections of resources");

            Stream requestStream = host.RequestStream;
            Debug.Assert(requestStream != null, "requestStream != null"); 
            object entity = null;
 
            ResourceType targetResourceType = description.TargetResourceType; 
            Debug.Assert(targetResourceType != null, "targetResourceType != null");
            if (!description.LinkUri && dataService.Provider.HasDerivedTypes(targetResourceType) && WebUtil.HasMediaLinkEntryInHierarchy(targetResourceType, dataService.Provider)) 
            {
                ResourceSetWrapper targetResourceSet = description.LastSegmentInfo.TargetContainer;
                Debug.Assert(targetResourceSet != null, "targetResourceSet != null");
                targetResourceType = dataService.StreamProvider.ResolveType(targetResourceSet.Name, dataService); 
                Debug.Assert(targetResourceType != null, "targetResourceType != null");
            } 
 
            UpdateTracker tracker = UpdateTracker.CreateUpdateTracker(dataService);
            if (!description.LinkUri && targetResourceType.IsMediaLinkEntry) 
            {
                // Verify that the user has rights to add to the target container
                Debug.Assert(description.LastSegmentInfo.TargetContainer != null, "description.LastSegmentInfo.TargetContainer != null");
                DataServiceConfiguration.CheckResourceRights(description.LastSegmentInfo.TargetContainer, EntitySetRights.WriteAppend); 

                entity = Deserializer.CreateMediaLinkEntry(targetResourceType.FullName, requestStream, dataService, description, tracker); 
                if (description.TargetSource == RequestTargetSource.Property) 
                {
                    Debug.Assert(description.Property.Kind == ResourcePropertyKind.ResourceSetReference, "Expecting POST resource set property"); 
                    Deserializer.HandleBindOperation(description, entity, dataService, tracker);
                }
            }
            else 
            {
                using (Deserializer deserializer = Deserializer.CreateDeserializer(description, dataService, false /*update*/, tracker)) 
                { 
                    entity = deserializer.HandlePostRequest(description);
                    Debug.Assert(entity != null, "entity != null"); 
                }
            }

            tracker.FireNotifications(); 
            return RequestDescription.CreateSingleResultRequestDescription(
                description, entity, description.LastSegmentInfo.TargetContainer); 
        } 

        /// Handles PUT requests. 
        /// description about the target request
        /// data service to which the request was made.
        /// new request description which contains the info about the entity resource getting modified.
        private static RequestDescription HandlePutOperation(RequestDescription description, IDataService dataService) 
        {
            Debug.Assert(description.TargetSource != RequestTargetSource.ServiceOperation, "description.TargetSource != RequestTargetSource.ServiceOperation"); 
            DataServiceHostWrapper host = dataService.OperationContext.Host; 

            if (!description.IsSingleResult) 
            {
                throw DataServiceException.CreateMethodNotAllowed(
                    Strings.BadRequest_InvalidUriForPutOperation(host.AbsoluteRequestUri),
                    DataServiceConfiguration.GetAllowedMethods(dataService.Configuration, description)); 
            }
            else if (description.LinkUri && description.Property.Kind != ResourcePropertyKind.ResourceReference) 
            { 
                throw DataServiceException.CreateMethodNotAllowed(Strings.DataService_CannotUpdateSetReferenceLinks, XmlConstants.HttpMethodDelete);
            } 

            // Note that for Media Resources, we let the Stream Provider decide whether or not to support If-None-Match for PUT
            if (!String.IsNullOrEmpty(host.RequestIfNoneMatch) && description.TargetKind != RequestTargetKind.MediaResource)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.DataService_IfNoneMatchHeaderNotSupportedInPut);
            } 
            else if (!RequestDescription.IsETagHeaderAllowed(description) && !String.IsNullOrEmpty(host.RequestIfMatch)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagCannotBeSpecified(host.AbsoluteRequestUri)); 
            }
            else if (description.Property != null && description.Property.IsOfKind(ResourcePropertyKind.Key))
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotUpdateKeyProperties(description.Property.Name)); 
            }
 
            Stream requestStream = host.RequestStream; 
            Debug.Assert(requestStream != null, "requestStream != null");
 
            return Deserializer.HandlePutRequest(description, dataService, requestStream);
        }

        /// Handles DELETE requests. 
        /// description about the target request
        /// data service to which the request was made. 
        private static void HandleDeleteOperation(RequestDescription description, IDataService dataService) 
        {
            Debug.Assert(description != null, "description != null"); 
            Debug.Assert(description.TargetSource != RequestTargetSource.ServiceOperation, "description.TargetSource != RequestTargetSource.ServiceOperation");
            Debug.Assert(dataService != null, "dataService != null");
            Debug.Assert(dataService.Configuration != null, "dataService.Configuration != null");
            Debug.Assert(dataService.OperationContext.Host != null, "dataService.OperationContext.Host != null"); 

            DataServiceHostWrapper host = dataService.OperationContext.Host; 
 
            // In general, deletes are only supported on resource referred via top level sets or collection properties.
            // If its the open property case, the key must be specified 
            // or you can unbind relationships using delete
            if (description.IsSingleResult && description.LinkUri)
            {
                HandleUnbindOperation(description, dataService); 
            }
            else if (description.IsSingleResult && description.TargetKind == RequestTargetKind.Resource) 
            { 
                Debug.Assert(description.LastSegmentInfo.TargetContainer != null, "description.LastSegmentInfo.TargetContainer != null");
 
                if (description.RequestEnumerable == null)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation);
                } 

                // 
                if (!String.IsNullOrEmpty(host.RequestIfNoneMatch)) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.DataService_IfNoneMatchHeaderNotSupportedInDelete); 
                }

                // Get the single entity result
                // We have to query for the delete case, since we don't know the type of the resource 
                object entity = Deserializer.GetResource(description.LastSegmentInfo, null, dataService, true /*checkForNull*/);
                ResourceSetWrapper container = description.LastSegmentInfo.TargetContainer; 
 
                // Need to check etag for DELETE operation
                dataService.Updatable.SetETagValues(entity, container); 

                //

                object actualEntity = dataService.Updatable.ResolveResource(entity); 

                ResourceType resourceType = dataService.Provider.GetResourceType(actualEntity); 
                if (description.Property != null) 
                {
                    Debug.Assert(container != null, "container != null"); 
                    DataServiceConfiguration.CheckResourceRights(container, EntitySetRights.WriteDelete);
                }

                dataService.Updatable.DeleteResource(entity); 

                if (resourceType != null && resourceType.IsMediaLinkEntry) 
                { 
                    dataService.StreamProvider.DeleteStream(actualEntity, dataService.OperationContext);
                } 

                UpdateTracker.FireNotification(dataService, actualEntity, container, UpdateOperations.Delete);
            }
            else if (description.TargetKind == RequestTargetKind.PrimitiveValue) 
            {
                Debug.Assert(description.TargetSource == RequestTargetSource.Property, "description.TargetSource == RequestTargetSource.Property"); 
                Debug.Assert(description.IsSingleResult, "description.IsSingleResult"); 

                // 
                if (!String.IsNullOrEmpty(host.RequestIfNoneMatch))
                {
                    throw DataServiceException.CreateBadRequestError(Strings.DataService_IfNoneMatchHeaderNotSupportedInDelete);
                } 

                if (description.Property != null && description.Property.IsOfKind(ResourcePropertyKind.Key)) 
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotUpdateKeyProperties(description.Property.Name));
                } 
                else if (description.Property.Type.IsValueType)
                {
                    // 403 - Forbidden
                    throw new DataServiceException(403, Strings.BadRequest_CannotNullifyValueTypeProperty); 
                }
 
                // We have to issue the query to get the resource 
                object securityResource;        // Resource on which security check can be made (possibly entity parent of 'resource').
                ResourceSetWrapper container;    // resource set to which the parent entity belongs to. 
                object resource = Deserializer.GetResourceToModify(description, dataService, false /*allowCrossReference*/, out securityResource, out container, true /*checkETag*/);

                object actualEntity = dataService.Updatable.ResolveResource(securityResource);
 
                // Doesn't matter which content format we pass here, since the value we are setting to is null
                Deserializer.ModifyResource(description, resource, null, ContentFormat.Text, dataService); 
 
                UpdateTracker.FireNotification(dataService, actualEntity, container, UpdateOperations.Change);
            } 
            else if (description.TargetKind == RequestTargetKind.OpenProperty)
            {
                // Open navigation properties are not supported on OpenTypes.
                throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(description.LastSegmentInfo.Identifier)); 
            }
            else if (description.TargetKind == RequestTargetKind.OpenPropertyValue) 
            { 
                //
                if (!String.IsNullOrEmpty(host.RequestIfNoneMatch)) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.DataService_IfNoneMatchHeaderNotSupportedInDelete);
                }
 
                object securityResource;
                ResourceSetWrapper container; 
                object resource = Deserializer.GetResourceToModify(description, dataService, false /*allowCrossReference*/, out securityResource, out container, true /*checkETag*/); 

                object actualEntity = dataService.Updatable.ResolveResource(securityResource); 

                // Doesn't matter which content format we pass here, since the value we are setting to is null
                Deserializer.ModifyResource(description, resource, null, ContentFormat.Text, dataService);
 
                UpdateTracker.FireNotification(dataService, actualEntity, container, UpdateOperations.Change);
            } 
            else 
            {
                throw DataServiceException.CreateMethodNotAllowed( 
                    Strings.BadRequest_InvalidUriForDeleteOperation(host.AbsoluteRequestUri),
                    DataServiceConfiguration.GetAllowedMethods(dataService.Configuration, description));
            }
        } 

        /// Handles a request for an internal resource if applicable. 
        /// Request description. 
        /// data service to which the request was made.
        ///  
        /// An action that produces the resulting stream; null if the description isn't for an internal resource.
        /// 
        private static Action HandleInternalResources(RequestDescription description, IDataService dataService)
        { 
            string[] exactContentType = null;
            ContentFormat format = ContentFormat.Unknown; 
            string mime = null; 
            DataServiceHostWrapper host = dataService.OperationContext.Host;
 
            if (description.TargetKind == RequestTargetKind.Metadata)
            {
                exactContentType = new string[] { XmlConstants.MimeMetadata };
                format = ContentFormat.MetadataDocument; 
                mime = HttpProcessUtility.SelectRequiredMimeType(
                    host.RequestAccept,   // acceptTypesText 
                    exactContentType,                   // exactContentType 
                    XmlConstants.MimeApplicationXml);   // inexactContentType
            } 
            else if (description.TargetKind == RequestTargetKind.ServiceDirectory)
            {
                exactContentType = new string[] { XmlConstants.MimeApplicationAtomService, XmlConstants.MimeApplicationJson, XmlConstants.MimeApplicationXml };
                mime = HttpProcessUtility.SelectRequiredMimeType( 
                    host.RequestAccept,   // acceptTypesText
                    exactContentType,                   // exactContentType 
                    XmlConstants.MimeApplicationXml);   // inexactContentType; 
                format = GetContentFormat(mime);
            } 

            if (exactContentType != null)
            {
                Debug.Assert( 
                    format != ContentFormat.Unknown,
                    "format(" + format + ") != ContentFormat.Unknown -- otherwise exactContentType should be null"); 
                Encoding encoding = HttpProcessUtility.EncodingFromAcceptCharset(host.RequestAcceptCharSet); 
                host.ResponseContentType = HttpProcessUtility.BuildContentType(mime, encoding);
                return new ResponseBodyWriter( 
                    encoding,
                    false,                  // hasMoved
                    dataService,
                    null,                   // queryResults 
                    description,
                    format).Write; 
            } 

            return null; 
        }

        /// 
        /// Compare the ETag value and then serialize the value if required 
        /// 
        /// Description of the uri requested. 
        /// Content format for response. 
        /// Data service to which the request was made.
        /// A delegate that can serialize the result. 
        private static Action CompareETagAndWriteResponse(
            RequestDescription description,
            ContentFormat responseFormat,
            IDataService dataService) 
        {
            Debug.Assert(description != null, "description != null"); 
            Debug.Assert(dataService != null, "dataService != null"); 
            Debug.Assert(dataService.OperationContext != null && dataService.OperationContext.Host != null, "dataService.OperationContext != null && dataService.OperationContext.Host != null");
            DataServiceHostWrapper host = dataService.OperationContext.Host; 
            Debug.Assert(
                String.IsNullOrEmpty(host.RequestIfMatch) || String.IsNullOrEmpty(host.RequestIfNoneMatch),
                "Both If-Match and If-None-Match header cannot be specified");
            IEnumerator queryResults = null; 
            try
            { 
                if (host.AstoriaHttpVerb == AstoriaVerbs.GET) 
                {
                    bool writeResponse = true; 

                    // Get the index of the last resource in the request uri
                    int parentResourceIndex = description.GetIndexOfTargetEntityResource();
                    Debug.Assert(parentResourceIndex >= 0 && parentResourceIndex < description.SegmentInfos.Length, "parentResourceIndex >= 0 && parentResourceIndex < description.SegmentInfos.Length"); 

                    SegmentInfo parentEntitySegment = description.SegmentInfos[parentResourceIndex]; 
                    queryResults = RequestDescription.GetSingleResultFromEnumerable(parentEntitySegment); 
                    object resource = queryResults.Current;
                    string etagValue = null; 

                    if (description.LinkUri)
                    {
                        // This must be already checked in SerializeResponseBody method. 
                        Debug.Assert(String.IsNullOrEmpty(host.RequestIfMatch) && String.IsNullOrEmpty(host.RequestIfNoneMatch), "ETag cannot be specified for $link requests");
                        if (resource == null) 
                        { 
                            throw DataServiceException.CreateResourceNotFound(description.LastSegmentInfo.Identifier);
                        } 
                    }
                    else if (RequestDescription.IsETagHeaderAllowed(description) && description.TargetKind != RequestTargetKind.MediaResource)
                    {
                        // Media Resources have their own ETags, we let the Stream Provider handle it. No need to compare the MLE ETag here. 
                        ResourceSetWrapper container = parentEntitySegment.TargetContainer;
                        Debug.Assert(container != null, "container != null"); 
 
                        etagValue = WebUtil.CompareAndGetETag(resource, resource, container, dataService, out writeResponse);
                    } 

                    if (resource == null && description.TargetKind == RequestTargetKind.Resource)
                    {
                        Debug.Assert(description.Property != null, "non-open type property"); 

                        WebUtil.Dispose(queryResults); 
                        queryResults = null; 

                        // If you are querying reference nav property and the value is null, 
                        // return 204 - No Content e.g. /Customers(1)/BestFriend
                        host.ResponseStatusCode = 204; // No Content
                        return WebUtil.GetEmptyStreamWriter();
                    } 

                    if (writeResponse) 
                    { 
                        return WriteSingleElementResponse(description, responseFormat, queryResults, parentResourceIndex, etagValue, dataService);
                    } 
                    else
                    {
                        WebUtil.Dispose(queryResults);
                        queryResults = null; 
#if DEBUG
                        WebUtil.WriteETagValueInResponseHeader(description, etagValue, host); 
#else 
                        WebUtil.WriteETagValueInResponseHeader(etagValue, host);
#endif 
                        host.ResponseStatusCode = 304; // Not Modified
                        return WebUtil.GetEmptyStreamWriter();
                    }
                } 
                else
                { 
                    Debug.Assert(host.AstoriaHttpVerb == AstoriaVerbs.POST, "Must be POST method"); 
                    ResourceSetWrapper container;
                    object actualEntity = GetContainerAndActualEntityInstance(dataService, description, out container); 
                    host.ResponseLocation = Serializer.GetUri(actualEntity, dataService.Provider, container, host.AbsoluteServiceUri).AbsoluteUri;
                    string etagValue = WebUtil.GetETagValue(dataService, actualEntity, container);
                    queryResults = RequestDescription.GetSingleResultFromEnumerable(description.LastSegmentInfo);
                    return WriteSingleElementResponse(description, responseFormat, queryResults, description.SegmentInfos.Length - 1, etagValue, dataService); 
                }
            } 
            catch 
            {
                WebUtil.Dispose(queryResults); 
                throw;
            }
        }
 
        /// Resolves the content format required when it is statically unknown.
        /// Request description. 
        /// Result target. 
        /// data service to which the request was made.
        /// The format for the specified element. 
        private static ContentFormat ResolveUnknownFormat(RequestDescription description, object element, IDataService dataService)
        {
            Debug.Assert(
                description.TargetKind == RequestTargetKind.OpenProperty || 
                description.TargetKind == RequestTargetKind.OpenPropertyValue ||
                description.TargetKind == RequestTargetKind.MediaResource, 
                description.TargetKind + " is open property, open property value, or MediaResource."); 
            WebUtil.CheckResourceExists(element != null, description.LastSegmentInfo.Identifier);
            ResourceType resourceType = WebUtil.GetResourceType(dataService.Provider, element); 
            Debug.Assert(resourceType != null, "resourceType != null, WebUtil.GetResourceType() should throw if it fails to resolve the resource type.");
            DataServiceHostWrapper host = dataService.OperationContext.Host;

            // Determine the appropriate target type based on the kind of resource. 
            bool rawValue = description.TargetKind == RequestTargetKind.OpenPropertyValue || description.TargetKind == RequestTargetKind.MediaResource;
            RequestTargetKind targetKind; 
            switch (resourceType.ResourceTypeKind) 
            {
                case ResourceTypeKind.ComplexType: 
                    if (rawValue)
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ValuesCanBeReturnedForPrimitiveTypesOnly);
                    } 
                    else
                    { 
                        targetKind = RequestTargetKind.ComplexObject; 
                    }
 
                    break;
                case ResourceTypeKind.Primitive:
                    if (rawValue)
                    { 
                        targetKind = RequestTargetKind.PrimitiveValue;
                    } 
                    else 
                    {
                        targetKind = RequestTargetKind.Primitive; 
                    }

                    break;
                default: 
                    Debug.Assert(ResourceTypeKind.EntityType == resourceType.ResourceTypeKind, "ResourceTypeKind.EntityType == " + resourceType.ResourceTypeKind);
                    if (rawValue) 
                    { 
                        if (resourceType.IsMediaLinkEntry)
                        { 
                            return SelectMediaResourceContentType(element, host.RequestAccept, dataService);
                        }
                        else
                        { 
                            throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidUriForMediaResource(host.AbsoluteRequestUri));
                        } 
                    } 
                    else
                    { 
                        targetKind = RequestTargetKind.Resource;
                    }

                    break; 
            }
 
            if (description.LinkUri) 
            {
                targetKind = RequestTargetKind.Link; 
            }

            return SelectResponseFormatForType(targetKind, resourceType, host.RequestAccept, null, dataService);
        } 

        ///  
        /// Compare the ETag value and then serialize the value if required 
        /// 
        /// Description of the uri requested. 
        /// format of the response
        /// Enumerator whose current resource points to the resource which needs to be written
        /// index of the segment info that represents the last resource
        /// etag value for the resource specified in parent resource parameter 
        /// data service to which the request was made.
        /// A delegate that can serialize the result. 
        private static Action WriteSingleElementResponse( 
            RequestDescription description,
            ContentFormat responseFormat, 
            IEnumerator queryResults,
            int parentResourceIndex,
            string etagValue,
            IDataService dataService) 
        {
            try 
            { 
                // The queryResults parameter contains the enumerator of the parent resource. If the parent resource's RequestEnumerable is not
                // the same instance as that of the last segment, we need to get the enumerator for the last segment. 
                // Take MediaResource for example, the MLE is its parent resource, which is what we want to write out and we don't want to
                // query for another instance of the enumerator.
                if (description.SegmentInfos[parentResourceIndex].RequestEnumerable != description.LastSegmentInfo.RequestEnumerable)
                { 
                    object resource = queryResults.Current;
 
                    for (int segmentIdx = parentResourceIndex + 1; segmentIdx < description.SegmentInfos.Length; segmentIdx++) 
                    {
                        SegmentInfo parentSegment = description.SegmentInfos[segmentIdx - 1]; 
                        SegmentInfo currentSegment = description.SegmentInfos[segmentIdx];

                        WebUtil.CheckResourceExists(resource != null, parentSegment.Identifier);
 
                        // $value has the same query as the preceding segment.
                        if (currentSegment.TargetKind == RequestTargetKind.PrimitiveValue || currentSegment.TargetKind == RequestTargetKind.OpenPropertyValue) 
                        { 
                            Debug.Assert(segmentIdx == description.SegmentInfos.Length - 1, "$value has to be the last segment.");
                            break; 
                        }

                        if (currentSegment.TargetKind == RequestTargetKind.OpenProperty)
                        { 
                            ResourceType openTypeParentResourceType = WebUtil.GetResourceType(dataService.Provider, resource);
 
                            if (openTypeParentResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) 
                            {
                                ResourceProperty resProperty = openTypeParentResourceType.Properties.First(p => p.Name == currentSegment.Identifier); 
                                resource = WebUtil.GetPropertyValue(dataService.Provider, resource, openTypeParentResourceType, resProperty, null);
                            }
                            else
                            { 
                                Debug.Assert(openTypeParentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "Entity Type expected");
                                resource = WebUtil.GetPropertyValue(dataService.Provider, resource, openTypeParentResourceType, null, currentSegment.Identifier); 
                            } 
                        }
                        else 
                        {
                            resource = WebUtil.GetPropertyValue(dataService.Provider, resource, parentSegment.TargetResourceType, currentSegment.ProjectedProperty, null);
                        }
                    } 

                    RequestDescription.CheckQueryResult(resource, description.LastSegmentInfo); 
 
                    queryResults = new QueryResultsWrapper((new object[] { resource }).GetEnumerator(), queryResults);
                    queryResults.MoveNext(); 
                }

                // If we had to wait until we got a value to determine the valid contents, try that now.
                if (responseFormat == ContentFormat.Unknown) 
                {
                    responseFormat = ResolveUnknownFormat(description, queryResults.Current, dataService); 
                } 

                Debug.Assert(responseFormat != ContentFormat.Unknown, "responseFormat != ContentFormat.Unknown"); 
                DataServiceHostWrapper host = dataService.OperationContext.Host;

                // Write the etag header
#if DEBUG 
                WebUtil.WriteETagValueInResponseHeader(description, etagValue, host);
#else 
                WebUtil.WriteETagValueInResponseHeader(etagValue, host); 
#endif
 
                Encoding encoding = GetRequestAcceptEncoding(responseFormat, host.RequestAcceptCharSet);
                host.ResponseContentType = HttpProcessUtility.BuildContentType(host.ResponseContentType, encoding);
                return new ResponseBodyWriter(
                    encoding, 
                    true /* hasMoved */,
                    dataService, 
                    queryResults, 
                    description,
                    responseFormat).Write; 
            }
            catch
            {
                WebUtil.Dispose(queryResults); 
                throw;
            } 
        } 

        ///  
        /// Returns the actual entity instance and its containers for the resource in the description results.
        /// 
        /// Data service
        /// description about the request made. 
        /// returns the container to which the result resource belongs to.
        /// returns the actual entity instance for the given resource. 
        private static object GetContainerAndActualEntityInstance( 
            IDataService service, RequestDescription description, out ResourceSetWrapper container)
        { 
            // For POST operations, we need to resolve the entity only after save changes. Hence we need to do this at the serialization
            // to make sure save changes has been called
            object[] results = (object[])description.RequestEnumerable;
            Debug.Assert(results != null && results.Length == 1, "results != null && results.Length == 1"); 

            // Make a call to the provider to get the exact resource instance back 
            results[0] = service.Updatable.ResolveResource(results[0]); 
            container = description.LastSegmentInfo.TargetContainer;
            if (container == null) 
            {
                // Open navigation properties are not supported on OpenTypes.
                throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(description.LastSegmentInfo.Identifier));
            } 

            Debug.Assert(container != null, "description.LastSegmentInfo.TargetContainer != null"); 
            return results[0]; 
        }
 
        /// 
        /// Handles the unbind operations
        /// 
        /// description about the request made. 
        /// data service to which the request was made.
        private static void HandleUnbindOperation(RequestDescription description, IDataService dataService) 
        { 
            Debug.Assert(description.LinkUri, "This method must be called for link operations");
            Debug.Assert(description.IsSingleResult, "Expecting this method to be called on single resource uris"); 

            if (!String.IsNullOrEmpty(dataService.OperationContext.Host.RequestIfMatch) || !String.IsNullOrEmpty(dataService.OperationContext.Host.RequestIfNoneMatch))
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagNotSupportedInUnbind); 
            }
 
            object parentEntity; 
            ResourceSetWrapper parentEntityResourceSet;
            Deserializer.GetResourceToModify(description, dataService, out parentEntity, out parentEntityResourceSet); 
            Debug.Assert(description.Property != null, "description.Property != null");
            if (description.Property.Kind == ResourcePropertyKind.ResourceReference)
            {
                dataService.Updatable.SetReference(parentEntity, description.Property.Name, null); 
            }
            else 
            { 
                Debug.Assert(description.Property.Kind == ResourcePropertyKind.ResourceSetReference, "expecting collection nav properties");
                Debug.Assert(description.LastSegmentInfo.HasKeyValues, "expecting properties to have key value specified"); 
                object childEntity = Deserializer.GetResource(description.LastSegmentInfo, null, dataService, true /*checkForNull*/);
                dataService.Updatable.RemoveReferenceFromCollection(parentEntity, description.Property.Name, childEntity);
            }
 
            if (dataService.Configuration.DataServiceBehavior.InvokeInterceptorsOnLinkDelete)
            { 
                object actualParentEntity = dataService.Updatable.ResolveResource(parentEntity); 
                UpdateTracker.FireNotification(dataService, actualParentEntity, parentEntityResourceSet, UpdateOperations.Change);
            } 
        }

        /// 
        /// Get the content format corresponding to the given mime type. 
        /// 
        /// mime type for the request. 
        /// content format mapping to the given mime type. 
        private static ContentFormat GetContentFormat(string mime)
        { 
            if (WebUtil.CompareMimeType(mime, XmlConstants.MimeApplicationJson))
            {
                return ContentFormat.Json;
            } 
            else if (WebUtil.CompareMimeType(mime, XmlConstants.MimeApplicationAtom))
            { 
                return ContentFormat.Atom; 
            }
            else 
            {
                Debug.Assert(
                    WebUtil.CompareMimeType(mime, XmlConstants.MimeApplicationXml) ||
                    WebUtil.CompareMimeType(mime, XmlConstants.MimeApplicationAtomService) || 
                    WebUtil.CompareMimeType(mime, XmlConstants.MimeTextXml),
                    "expecting application/xml, application/atomsvc+xml or plain/xml, got " + mime); 
                return ContentFormat.PlainXml; 
            }
        } 

        /// 
        /// Handle the request - whether its a batch request or a non-batch request
        ///  
        /// Returns the delegate for writing the response
        private Action HandleRequest() 
        { 
            Debug.Assert(this.operationContext != null, "this.operationContext != null");
 
            // Need to cache the request headers for every request. Note that the while the underlying
            // host instance may stay the same across requests, the request headers can change between
            // requests. We have to refresh the cache for every request.
            this.operationContext.InitializeAndCacheHeaders(); 
            Action writer = null;
 
            try 
            {
                this.EnsureProviderAndConfigForRequest(); 
            }
            catch (Exception ex)
            {
#if DEBUG 
                // We've hit an error, some of the processing pipeline events will not get fired. Setting this flag to skip validation.
                this.ProcessingPipeline.SkipDebugAssert = true; 
#endif 
                int responseStatusCode = 500;
                if (!WebUtil.IsCatchableExceptionType(ex)) 
                {
                    throw;
                }
 
                // if Exception been thrown is DSE, we keep the exception's status code
                // otherwise, the status code is 500. 
                DataServiceException dse = ex as DataServiceException; 
                if (dse != null)
                { 
                    responseStatusCode = dse.StatusCode;
                }

                // safe handling of initialization time error 
                DataServiceHostWrapper host = this.operationContext.Host;
                host.ResponseStatusCode = responseStatusCode; 
                host.ResponseVersion = XmlConstants.DataServiceVersion1Dot0 + ";"; 
                throw;
            } 

            try
            {
                RequestDescription description = this.ProcessIncomingRequestUri(); 
                if (description.TargetKind != RequestTargetKind.Batch)
                { 
                    writer = this.HandleNonBatchRequest(description); 

                    // Query Processing Pipeline - Request end event 
                    // Note 1 we only invoke the event handler for ALL operations
                    // Note 2 we invoke this event before serialization is complete
                    // Note 3 we invoke this event before any provider interface held by the data service runtime is released/disposed
                    DataServiceProcessingPipelineEventArgs eventArg = new DataServiceProcessingPipelineEventArgs(this.operationContext); 
                    this.processingPipeline.InvokeProcessedRequest(this, eventArg);
                } 
                else 
                {
                    writer = this.HandleBatchRequest(); 
                }
            }
            catch (Exception exception)
            { 
#if DEBUG
                // We've hit an error, some of the processing pipeline events will not get fired. Setting this flag to skip validation. 
                this.ProcessingPipeline.SkipDebugAssert = true; 
#endif
                // Exception should be re-thrown if not handled. 
                if (!WebUtil.IsCatchableExceptionType(exception))
                {
                    throw;
                } 

                string accept = (this.operationContext != null) ? this.operationContext.Host.RequestAccept : null; 
                string acceptCharset = (this.operationContext != null) ? this.operationContext.Host.RequestAcceptCharSet : null; 
                writer = ErrorHandler.HandleBeforeWritingException(exception, this, accept, acceptCharset);
            } 

            Debug.Assert(writer != null, "writer != null");
            return writer;
        } 

        ///  
        /// Handle non-batch requests 
        /// 
        /// description about the request uri. 
        /// Returns the delegate which takes the response stream for writing the response.
        private Action HandleNonBatchRequest(RequestDescription description)
        {
            Debug.Assert(description.TargetKind != RequestTargetKind.Batch, "description.TargetKind != RequestTargetKind.Batch"); 
            bool serviceOperationRequest = (description.TargetSource == RequestTargetSource.ServiceOperation);
 
            // The reason to create UpdatableWrapper here is to make sure that the right data service instance is 
            // passed to the UpdatableWrapper. Earlier this line used to live in EnsureProviderAndConfigForRequest
            // method, which means that same data service instance was passed to the UpdatableWrapper, irrespective 
            // of whether this was a batch request or not. The issue with that was in UpdatableWrapper.SetConcurrencyValues
            // method, if someone tried to access the service.RequestParams, this will give you the headers for the
            // top level batch request, not the part of the batch request we are processing.
            this.updatable = new UpdatableWrapper(this); 
            description = ProcessIncomingRequest(description, this);
 
            if (this.operationContext.Host.AstoriaHttpVerb != AstoriaVerbs.GET) 
            {
                // Bug 470090: Since we used to call SaveChanges() for service operations in V1, we need to 
                // keep doing that for V1 providers that implement IUpdatable. In other words, for ObjectContextServiceProvider
                // we will always do this, and for reflection service provider, we will have to check.
                if (serviceOperationRequest)
                { 
                    if (this.provider.IsV1ProviderAndImplementsUpdatable())
                    { 
                        this.updatable.SaveChanges(); 
                    }
                } 
                else
                {
                   this.updatable.SaveChanges();
                } 

                // Query Processing Pipeline - Changeset end event 
                // Note 1 we only invoke the event handler for CUD operations 
                // Note 2 we invoke this event immediately after SaveChanges()
                // Note 3 we invoke this event before serialization happens 
                this.processingPipeline.InvokeProcessedChangeset(this, new EventArgs());
            }

            return (description == null) ? WebUtil.GetEmptyStreamWriter() : SerializeResponseBody(description, this); 
        }
 
        /// Handle the batch request. 
        /// Returns the delegate which takes the response stream for writing the response.
        private Action HandleBatchRequest() 
        {
            Debug.Assert(this.operationContext != null && this.operationContext.Host != null, "this.operationContext != null && this.operationContext.Host != null");
            DataServiceHostWrapper host = this.operationContext.Host;
 
            // Verify the HTTP method.
            if (host.AstoriaHttpVerb != AstoriaVerbs.POST) 
            { 
                throw DataServiceException.CreateMethodNotAllowed(
                    Strings.DataService_BatchResourceOnlySupportsPost, 
                    XmlConstants.HttpMethodPost);
            }

            WebUtil.CheckVersion(this, null); 

            // Verify the content type and get the boundary string 
            Encoding encoding; 
            string boundary;
 
            if (!BatchStream.GetBoundaryAndEncodingFromMultipartMixedContentType(host.RequestContentType, out boundary, out encoding) ||
                String.IsNullOrEmpty(boundary))
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_InvalidContentTypeForBatchRequest); 
            }
 
            // Write the response headers 
            host.ResponseStatusCode = 202; // OK
            host.ResponseCacheControl = XmlConstants.HttpCacheControlNoCache; 

            string batchBoundary = XmlConstants.HttpMultipartBoundaryBatchResponse + '_' + Guid.NewGuid().ToString();
            host.ResponseContentType = String.Format(
                System.Globalization.CultureInfo.InvariantCulture, 
                "{0}; {1}={2}",
                XmlConstants.MimeMultiPartMixed, 
                XmlConstants.HttpMultipartBoundary, 
                batchBoundary);
 
            // DEVNOTE([....]):
            // Added for V2+ services
            // The batch response format should be 1.0 until we update the format itself
            // Each individual batch response will set its version to the batch host. 
            host.ResponseVersion = XmlConstants.DataServiceVersion1Dot0 + ";";
 
            BatchStream batchStream = new BatchStream(host.RequestStream, boundary, encoding, true); 
            BatchDataService batchDataService = new BatchDataService(this, batchStream, batchBoundary);
            return batchDataService.HandleBatchContent; 
        }

        /// Creates the provider and configuration as necessary to be used for this request.
        private void EnsureProviderAndConfigForRequest() 
        {
            if (this.provider == null) 
            { 
                this.CreateProvider();
            } 
            else
            {
                Debug.Assert(this.configuration != null, "this.configuration != null -- otherwise this.provider was ----signed with no configuration");
            } 

#if DEBUG 
            // No event should be fired before this point. 
            // No provider interfaces except IDSP should be created before this point.
            this.processingPipeline.AssertInitialDebugState(); 
#endif
        }

        ///  
        /// Creates a provider implementation that wraps the T type.
        ///  
        private void CreateProvider() 
        {
            // From the IDSP Spec: 
            // If the class derived from DataService implements IServiceProvider then:
            // a. Invoke
            //    IDataServiceMetadataProvider provider = IServiceProvider.GetService(TypeOf(IDataMetadataServiceProvider))
            // 
            // b. If provider != null, then the service is using a Custom provider (ie. custom implementation of IDSP)
            //    Note: DataService.CreateDataSource is NOT invoked for custom data service providers 
            // 
            // c. If provider == null, then:
            //    i.   Create an instance of T by invoking DataService.CreateDataSource (this method may be overridden by a service author) 
            //    ii.  If T implements IDataServiceMetadataProvider then the service is using a custom data service provider (skip step iii. & iv.)
            //    iii. If typeof(T) == typeof(System.Data.Objects.ObjectContext) then the service will use the built-in IDSP implementation for EF (ie. the �EF provider�)
            //    iv.  If typeof(T) != typeof(System.Data.Objects.ObjectContext) then the service will use the built-in reflection for arbitrary .NET classes (ie. the �reflection provider�)
            Type dataServiceType = this.GetType(); 
            Type dataContextType = typeof(T);
 
            bool friendlyFeedsV1Compatible; 

            // If the GetService call returns a provider, that means there is a custom implementation of the provider 
            IDataServiceMetadataProvider metadataProviderInstance = WebUtil.GetService(this);
            IDataServiceQueryProvider queryProviderInstance = null;
            object dataSourceInstance = null;
            if (metadataProviderInstance != null) 
            {
                queryProviderInstance = WebUtil.GetService(this); 
                if (queryProviderInstance == null) 
                {
                    throw new InvalidOperationException(Strings.DataService_IDataServiceQueryProviderNull); 
                }

                // For custom providers, we will first query the queryProvider to check if the provider returns a data source instance.
                // If it doesn't, then we will create a instance of data source and pass it to the query provider. 
                dataSourceInstance = queryProviderInstance.CurrentDataSource;
                if (dataSourceInstance == null) 
                { 
                    dataSourceInstance = this.CreateDataSourceInstance();
                    queryProviderInstance.CurrentDataSource = dataSourceInstance; 
                }

                if (!dataContextType.IsAssignableFrom(dataSourceInstance.GetType()))
                { 
                    throw new InvalidOperationException(Strings.DataServiceProviderWrapper_DataSourceTypeMustBeAssignableToContextType);
                } 
            } 
            else
            { 
                // Create the data source from the service by calling DataService.CreateDataSource
                dataSourceInstance = this.CreateDataSourceInstance();

                // Try if the data source implements IDSMP 
                metadataProviderInstance = dataSourceInstance as IDataServiceMetadataProvider;
                if (metadataProviderInstance != null) 
                { 
                    queryProviderInstance = dataSourceInstance as IDataServiceQueryProvider;
                    if (queryProviderInstance == null) 
                    {
                        throw new InvalidOperationException(Strings.DataService_IDataServiceQueryProviderNull);
                    }
 
                    // For customer providers if we already have the data source instance, we will pass it to the query provider.
                    queryProviderInstance.CurrentDataSource = dataSourceInstance; 
                } 
            }
 
            // If we found IDSMP by now - it means we will use custom provider, otherwise we will use one of our built-in providers
            if (metadataProviderInstance != null)
            {
                Debug.Assert(queryProviderInstance != null, "If we have IDSMP we should also have IDSQP."); 

                // For IDSMP, we cache the configuration object and we must NOT cache any of the metadata objects. 
                // This means we call InitializeService() once per service instead of once per request. 
                MetadataCacheItem metadata = MetadataCache.TryLookup(dataServiceType, dataSourceInstance);
                bool metadataRequiresInitialization = metadata == null; 
                if (metadataRequiresInitialization)
                {
                    metadata = new MetadataCacheItem(dataContextType);
                    metadata.Configuration = CreateConfiguration(dataServiceType, metadataProviderInstance); 
                    MetadataCache.AddCacheItem(dataServiceType, dataSourceInstance, metadata);
                } 
 
                Debug.Assert(metadata != null, "Metadata item should have been found or created within this function");
                this.configuration = metadata.Configuration; 

                // For IDSMP, we cannot cache anything except for the configuration.
                // We need to pass in a new MetadataCacheItem instance for each request.
                metadata = new MetadataCacheItem(dataContextType) { Configuration = this.configuration }; 
                this.provider = new DataServiceProviderWrapper(metadata, metadataProviderInstance, queryProviderInstance);
 
                // For IDSMP we have to assume that friendly feeds are V1 compatible. We will validate this assumption 
                // when processing requests and throw an exception if this happens to be false.
                friendlyFeedsV1Compatible = true; 
            }
            else
            {
                MetadataCacheItem metadata = MetadataCache.TryLookup(dataServiceType, dataSourceInstance); 
                bool metadataRequiresInitialization = metadata == null;
                if (metadataRequiresInitialization) 
                { 
                    metadata = new MetadataCacheItem(dataContextType);
                } 

                BaseServiceProvider dataProviderInstance;

                // use our built-in providers and policy layer 
                if (typeof(ObjectContext).IsAssignableFrom(dataContextType))
                { 
                    dataProviderInstance = new ObjectContextServiceProvider(metadata, this); 
                }
                else 
                {
                    dataProviderInstance = new ReflectionServiceProvider(metadata, this);
                }
 
                dataProviderInstance.CurrentDataSource = dataSourceInstance;
                this.provider = new DataServiceProviderWrapper(metadata, dataProviderInstance, dataProviderInstance); 
                dataProviderInstance.ProviderWrapper = this.provider; 

                if (metadataRequiresInitialization) 
                {
                    // Populate metadata in provider.
                    dataProviderInstance.PopulateMetadata();
                    dataProviderInstance.AddOperationsFromType(dataServiceType); 

                    // Create and cache configuration, which goes hand-in-hand with metadata. 
                    metadata.Configuration = CreateConfiguration(dataServiceType, dataProviderInstance); 

                    // Apply the access rights info from the configuration. 
                    dataProviderInstance.ApplyConfiguration(metadata.Configuration);

                    // After all the operations are done, make metadata readonly.
                    dataProviderInstance.MakeMetadataReadonly(); 

                    // Populate and cache the metadata. 
                    this.provider.PopulateMetadataCacheItemForV1Provider(); 
                    MetadataCache.AddCacheItem(dataServiceType, dataSourceInstance, metadata);
                } 

                this.configuration = metadata.Configuration;

                friendlyFeedsV1Compatible = metadata.EpmIsV1Compatible; 
            }
 
            this.configuration.ValidateServerOptions(friendlyFeedsV1Compatible); 

            Debug.Assert(this.configuration != null, "configuration != null"); 
            Debug.Assert(this.provider != null, "wrapper != null");
        }

        ///  
        /// Processes the incoming request and cache all the request headers
        ///  
        /// description about the request uri. 
        private RequestDescription ProcessIncomingRequestUri()
        { 
            Debug.Assert(
                this.operationContext != null && this.operationContext.Host != null,
                "this.operationContext != null && this.operationContext.Host != null");
            DataServiceHostWrapper host = this.operationContext.Host; 

            // Validation of query parameters must happen only after the request parameters have been cached, 
            // otherwise we might not serialize the errors in the correct serialization format. 
            host.VerifyQueryParameters();
 
            ValidateRequest(this.operationContext);

            // Query Processing Pipeline - Request start event
            DataServiceProcessingPipelineEventArgs eventArg = new DataServiceProcessingPipelineEventArgs(this.operationContext); 
            this.processingPipeline.InvokeProcessingRequest(this, eventArg);
 
            // V1 OnStartProcessingRequest(). 
            ((IDataService)this).InternalOnStartProcessingRequest(new ProcessRequestArgs(host.AbsoluteRequestUri, false /*isBatchOperation*/, this.operationContext));
 
            // Query Processing Pipeline - Changeset start event
            // Note 1 we only invoke the event handler for CUD operations
            // Note 2 for a batch request this event will be invoked when we process the changeset boundary
            if (host.AstoriaHttpVerb != AstoriaVerbs.GET && !this.operationContext.IsBatchRequest) 
            {
                this.processingPipeline.InvokeProcessingChangeset(this, new EventArgs()); 
            } 

            return RequestUriProcessor.ProcessRequestUri(host.AbsoluteRequestUri, this); 
        }

        /// 
        /// Create the data source instance by calling the CreateDataSource virtual method 
        /// 
        /// returns the instance of the data source. 
        private object CreateDataSourceInstance() 
        {
            object dataSourceInstance = this.CreateDataSource(); 
            if (dataSourceInstance == null)
            {
                throw new InvalidOperationException(Strings.DataService_CreateDataSourceNull);
            } 

            return dataSourceInstance; 
        } 

        #endregion Private methods. 

        /// 
        /// Dummy data service for batch requests
        ///  
        private class BatchDataService : IDataService
        { 
            #region Private fields. 

            /// Original data service instance. 
            private readonly IDataService dataService;

            /// batch stream which reads the content of the batch from the underlying request stream.
            private readonly BatchStream batchRequestStream; 

            /// batch response seperator string. 
            private readonly string batchBoundary; 

            /// Hashset to make sure that the content ids specified in the batch are all unique. 
            private readonly HashSet contentIds = new HashSet(new Int32EqualityComparer());

            /// Dictionary to track objects represented by each content id within a changeset.
            private readonly Dictionary contentIdsToSegmentInfoMapping = new Dictionary(StringComparer.Ordinal); 

            /// Number of changset/query operations encountered in the current batch. 
            private int batchElementCount; 

            /// Whether the batch limit has been exceeded (implies no further processing should take place). 
            private bool batchLimitExceeded;

            /// List of the all request description within a changeset.
            private List batchRequestDescription = new List(); 

            /// List of the all response headers and results of each operation within a changeset. 
            private List batchOperationContexts = new List(); 

            /// Number of CUD operations encountered in the current changeset. 
            private int changeSetElementCount;

            /// The context of the current batch operation.
            private DataServiceOperationContext operationContext; 

            /// Instance which implements IUpdatable interface. 
            private UpdatableWrapper updatable; 

            /// Instance which implements the IDataServicePagingProvider interface. 
            private DataServicePagingProviderWrapper pagingProvider;

            /// Instance which implements IDataServiceStreamProvider interface.
            private DataServiceStreamProviderWrapper streamProvider; 

            #endregion Private fields. 
 
            /// 
            /// Creates an instance of the batch data service which keeps track of the 
            /// request and response headers per operation in the batch
            /// 
            /// original data service to which the batch request was made
            /// batch stream which read batch content from the request stream 
            /// batch response seperator string.
            internal BatchDataService(IDataService dataService, BatchStream batchRequestStream, string batchBoundary) 
            { 
                Debug.Assert(dataService != null, "dataService != null");
                Debug.Assert(batchRequestStream != null, "batchRequestStream != null"); 
                Debug.Assert(batchBoundary != null, "batchBoundary != null");
                this.dataService = dataService;
                this.batchRequestStream = batchRequestStream;
                this.batchBoundary = batchBoundary; 
            }
 
            #region IDataService Members 

            /// Service configuration information. 
            public DataServiceConfiguration Configuration
            {
                get { return this.dataService.Configuration; }
            } 

            /// Data provider for this data service. 
            public DataServiceProviderWrapper Provider 
            {
                get { return this.dataService.Provider; } 
            }

            /// IUpdatable interface for this provider
            public UpdatableWrapper Updatable 
            {
                get { return this.updatable ?? (this.updatable = new UpdatableWrapper(this)); } 
            } 

            /// IDataServicePagingProvider wrapper object. 
            public DataServicePagingProviderWrapper PagingProvider
            {
                get { return this.pagingProvider ?? (this.pagingProvider = new DataServicePagingProviderWrapper(this)); }
            } 

            /// Instance which implements IDataServiceStreamProvider interface. 
            public DataServiceStreamProviderWrapper StreamProvider 
            {
                get { return this.streamProvider ?? (this.streamProvider = new DataServiceStreamProviderWrapper(this)); } 
            }

            /// Instance of the data provider.
            public object Instance 
            {
                get { return this.dataService.Instance; } 
            } 

            /// Gets the context of the current batch operation. 
            public DataServiceOperationContext OperationContext
            {
                get { return this.operationContext; }
            } 

            /// Processing pipeline events 
            public DataServiceProcessingPipeline ProcessingPipeline 
            {
                get { return this.dataService.ProcessingPipeline; } 
            }

            /// 
            /// This method is called during query processing to validate and customize 
            /// paths for the $expand options are applied by the provider.
            ///  
            /// Query which will be composed. 
            /// Collection of segment paths to be expanded.
            public void InternalApplyingExpansions(IQueryable queryable, ICollection expandPaths) 
            {
                this.dataService.InternalApplyingExpansions(queryable, expandPaths);
            }
 
            /// Processes a catchable exception.
            /// The arguments describing how to handle the exception. 
            public void InternalHandleException(HandleExceptionArgs args) 
            {
                this.dataService.InternalHandleException(args); 
            }

#if ASTORIA_FF_CALLBACKS
            ///  
            /// Invoked once feed has been written to override the feed elements
            ///  
            /// Feed being written 
            void IDataService.InternalOnWriteFeed(SyndicationFeed feed)
            { 
                this.dataService.InternalOnWriteFeed(feed);
            }

            ///  
            /// Invoked once an element has been written to override the element
            ///  
            /// Item that has been written 
            /// Object with content for the 
            void IDataService.InternalOnWriteItem(SyndicationItem item, object obj) 
            {
                this.dataService.InternalOnWriteItem(item, obj);
            }
#endif 
            /// 
            /// Returns the segmentInfo of the resource referred by the given content Id; 
            ///  
            /// content id for a operation in the batch request.
            /// segmentInfo for the resource referred by the given content id. 
            public SegmentInfo GetSegmentForContentId(string contentId)
            {
                if (contentId.StartsWith("$", StringComparison.Ordinal))
                { 
                    SegmentInfo segmentInfo;
                    this.contentIdsToSegmentInfoMapping.TryGetValue(contentId.Substring(1), out segmentInfo); 
                    return segmentInfo; 
                }
 
                return null;
            }

            ///  
            /// Get the resource referred by the segment in the request with the given index
            ///  
            /// description about the request url. 
            /// index of the segment that refers to the resource that needs to be returned.
            /// typename of the resource. 
            /// the resource as returned by the provider.
            public object GetResource(RequestDescription description, int segmentIndex, string typeFullName)
            {
                if (Deserializer.IsCrossReferencedSegment(description.SegmentInfos[0], this)) 
                {
                    Debug.Assert(segmentIndex >= 0 && segmentIndex < description.SegmentInfos.Length, "segment index must be a valid one"); 
                    if (description.SegmentInfos[segmentIndex].RequestEnumerable == null) 
                    {
                        object resource = Deserializer.GetCrossReferencedResource(description.SegmentInfos[0]); 
                        for (int i = 1; i <= segmentIndex; i++)
                        {
                            resource = this.Updatable.GetValue(resource, description.SegmentInfos[i].Identifier);
                            if (resource == null) 
                            {
                                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DereferencingNullPropertyValue(description.SegmentInfos[i].Identifier)); 
                            } 

                            description.SegmentInfos[i].RequestEnumerable = new object[] { resource }; 
                        }

                        return resource;
                    } 
                    else
                    { 
                        return Deserializer.GetCrossReferencedResource(description.SegmentInfos[segmentIndex]); 
                    }
                } 

                return Deserializer.GetResource(description.SegmentInfos[segmentIndex], typeFullName, this, false /*checkForNull*/);
            }
 
            /// 
            /// Dispose the data source instance 
            ///  
            public void DisposeDataSource()
            { 
#if DEBUG
                this.dataService.ProcessingPipeline.AssertDebugStateAtDispose();
                this.dataService.ProcessingPipeline.HasDisposedProviderInterfaces = true;
#endif 
                if (this.updatable != null)
                { 
                    this.updatable.DisposeProvider(); 
                    this.updatable = null;
                } 

                if (this.pagingProvider != null)
                {
                    this.pagingProvider.DisposeProvider(); 
                    this.pagingProvider = null;
                } 
 
                if (this.streamProvider != null)
                { 
                    this.streamProvider.DisposeProvider();
                    this.streamProvider = null;
                }
 
                this.dataService.DisposeDataSource();
            } 
 
            /// 
            /// This method is called before a request is processed. 
            /// 
            /// Information about the request that is going to be processed.
            public void InternalOnStartProcessingRequest(ProcessRequestArgs args)
            { 
                this.dataService.InternalOnStartProcessingRequest(args);
            } 
 
            #endregion
 
            /// 
            /// Handle the batch content
            /// 
            /// response stream for writing batch response 
            internal void HandleBatchContent(Stream responseStream)
            { 
                DataServiceOperationContext currentOperationContext = null; 
                RequestDescription description;
                string changesetBoundary = null; 
                Exception exceptionEncountered = null;
                bool serviceOperationRequests = true;

                try 
                {
                    // After we have completely read the request, we should not close 
                    // the request stream, since its owned by the underlying host. 
                    StreamWriter writer = new StreamWriter(responseStream, HttpProcessUtility.FallbackEncoding);
                    while (!this.batchLimitExceeded && this.batchRequestStream.State != BatchStreamState.EndBatch) 
                    {
                        // clear the context from the last operation
                        this.operationContext = null;
 
                        // If we encounter any error while reading the batch request,
                        // we write out the exception message and return. We do not try 
                        // and read the request further. 
                        try
                        { 
                            this.batchRequestStream.MoveNext();
                        }
                        catch (Exception exception)
                        { 
                            if (!WebUtil.IsCatchableExceptionType(exception))
                            { 
                                throw; 
                            }
 
                            ErrorHandler.HandleBatchRequestException(this, exception, writer);
                            break;
                        }
 
                        try
                        { 
                            switch (this.batchRequestStream.State) 
                            {
                                case BatchStreamState.BeginChangeSet: 
                                    this.IncreaseBatchCount();
                                    changesetBoundary = XmlConstants.HttpMultipartBoundaryChangesetResponse + '_' + Guid.NewGuid().ToString();
                                    BatchWriter.WriteStartBatchBoundary(writer, this.batchBoundary, changesetBoundary);
 
                                    // Query Processing Pipeline - Changeset start event
                                    // Note we only invoke the event handler for CUD operations 
                                    this.dataService.ProcessingPipeline.InvokeProcessingChangeset(this.dataService, new EventArgs()); 
                                    break;
 
                                case BatchStreamState.EndChangeSet:
                                    #region EndChangeSet
                                    this.changeSetElementCount = 0;
                                    this.contentIdsToSegmentInfoMapping.Clear(); 

                                    // In case of exception, the changeset boundary will be set to null. 
                                    // for that case, just write the end boundary and continue 
                                    if (exceptionEncountered == null && this.batchRequestDescription.Count > 0)
                                    { 
                                        Debug.Assert(!String.IsNullOrEmpty(changesetBoundary), "!String.IsNullOrEmpty(changesetBoundary)");

                                        // Bug 470090: We don't need to call SaveChanges if all requests in the changesets are requests to
                                        // service operations. But in V1, we used to call SaveChanges, we need to keep calling it, if its 
                                        // implemented.
                                        if (serviceOperationRequests) 
                                        { 
                                            if (this.Provider.IsV1ProviderAndImplementsUpdatable())
                                            { 
                                                this.Updatable.SaveChanges();
                                            }
                                        }
                                        else 
                                        {
                                            // Save all the changes and write the response 
                                            this.Updatable.SaveChanges(); 
                                        }
                                    } 

                                    if (exceptionEncountered == null)
                                    {
                                        // Query Processing Pipeline - Changeset end event 
                                        // Note 1 we only invoke the event handler for CUD operations
                                        // Note 2 we invoke this event immediately after SaveChanges() 
                                        // Note 3 we invoke this event before serialization happens 
                                        this.dataService.ProcessingPipeline.InvokeProcessedChangeset(this.dataService, new EventArgs());
 
                                        Debug.Assert(this.batchOperationContexts.Count == this.batchRequestDescription.Count, "counts must be the same");
                                        for (int i = 0; i < this.batchRequestDescription.Count; i++)
                                        {
                                            this.operationContext = this.batchOperationContexts[i]; 
                                            this.WriteRequest(this.batchRequestDescription[i], this.batchOperationContexts[i].Host.BatchServiceHost);
                                        } 
 
                                        BatchWriter.WriteEndBoundary(writer, changesetBoundary);
                                    } 
                                    else
                                    {
                                        this.HandleChangesetException(exceptionEncountered, this.batchOperationContexts, changesetBoundary, writer);
                                    } 

                                    break; 
                                    #endregion //EndChangeSet 
                                case BatchStreamState.Get:
                                    #region GET Operation 
                                    this.IncreaseBatchCount();
                                    currentOperationContext = CreateOperationContextFromBatchStream(
                                        this.dataService.OperationContext.AbsoluteServiceUri,
                                        this.batchRequestStream, 
                                        this.contentIds,
                                        this.batchBoundary, 
                                        writer); 
                                    this.operationContext = currentOperationContext;
 
                                    // it must be GET operation
                                    Debug.Assert(this.operationContext.Host.AstoriaHttpVerb == AstoriaVerbs.GET, "this.operationContext.Host.AstoriaHttpVerb == AstoriaVerbs.GET");
                                    Debug.Assert(this.batchRequestDescription.Count == 0, "this.batchRequestDescription.Count == 0");
                                    Debug.Assert(this.batchOperationContexts.Count == 0, "this.batchRequestHost.Count == 0"); 

                                    this.dataService.InternalOnStartProcessingRequest(new ProcessRequestArgs(this.operationContext.AbsoluteRequestUri, true /*isBatchOperation*/, this.operationContext)); 
                                    description = RequestUriProcessor.ProcessRequestUri(this.operationContext.AbsoluteRequestUri, this); 
                                    description = ProcessIncomingRequest(description, this);
                                    this.WriteRequest(description, currentOperationContext.Host.BatchServiceHost); 
                                    break;
                                    #endregion // GET Operation
                                case BatchStreamState.Post:
                                case BatchStreamState.Put: 
                                case BatchStreamState.Delete:
                                case BatchStreamState.Merge: 
                                    #region CUD Operation 
                                    // if we encounter an error, we ignore rest of the operations
                                    // within a changeset. 
                                    this.IncreaseChangeSetCount();
                                    currentOperationContext = CreateOperationContextFromBatchStream(this.dataService.OperationContext.AbsoluteServiceUri, this.batchRequestStream, this.contentIds, changesetBoundary, writer);
                                    if (exceptionEncountered == null)
                                    { 
                                        this.batchOperationContexts.Add(currentOperationContext);
                                        this.operationContext = currentOperationContext; 
 
                                        this.dataService.InternalOnStartProcessingRequest(new ProcessRequestArgs(this.operationContext.AbsoluteRequestUri, true /*isBatchOperation*/, this.operationContext));
                                        description = RequestUriProcessor.ProcessRequestUri(this.operationContext.AbsoluteRequestUri, this); 

                                        // If there are all batch requests in the changeset, then we don't need to call SaveChanges()
                                        serviceOperationRequests &= (description.TargetSource == RequestTargetSource.ServiceOperation);
 
                                        description = ProcessIncomingRequest(description, this);
                                        this.batchRequestDescription.Add(description); 
 
                                        // In Link case, we do not write any response out. hence the description will be null
                                        if (description != null) 
                                        {
                                            if (this.batchRequestStream.State == BatchStreamState.Post)
                                            {
                                                Debug.Assert( 
                                                        description.TargetKind == RequestTargetKind.Resource || description.TargetSource == RequestTargetSource.ServiceOperation,
                                                        "The target must be a resource or source should be a service operation, since otherwise cross-referencing doesn't make sense"); 
 
                                                // if the content id is specified, only then add it to the collection
                                                string contentId = currentOperationContext.Host.BatchServiceHost.ContentId; 
                                                if (contentId != null)
                                                {
                                                    this.contentIdsToSegmentInfoMapping.Add(contentId, description.LastSegmentInfo);
                                                } 
                                            }
                                            else if (this.batchRequestStream.State == BatchStreamState.Put) 
                                            { 
                                                // If this is a cross-referencing a previous POST resource, then we need to
                                                // replace the resource in the previous POST request with the new resource 
                                                // that the provider returned for this request so that while serializing out,
                                                // we will have the same instance for POST/PUT
                                                this.UpdateRequestEnumerableForPut(description);
                                            } 
                                        }
                                    } 
 
                                    break;
                                    #endregion // CUD Operation 
                                default:
                                    Debug.Assert(this.batchRequestStream.State == BatchStreamState.EndBatch, "expecting end batch state");

                                    // Query Processing Pipeline - Request end event 
                                    // Note 1 we only invoke the event handler for ALL operations
                                    // Note 2 we invoke this event before serialization is complete 
                                    // Note 3 we invoke this event before any provider interface held by the data service runtime is released/disposed 
                                    DataServiceProcessingPipelineEventArgs eventArg = new DataServiceProcessingPipelineEventArgs(this.dataService.OperationContext);
                                    this.dataService.ProcessingPipeline.InvokeProcessedRequest(this.dataService, eventArg); 
                                    break;
                            }
                        }
                        catch (Exception exception) 
                        {
                            if (!WebUtil.IsCatchableExceptionType(exception)) 
                            { 
                                throw;
                            } 

                            if (this.batchRequestStream.State == BatchStreamState.EndChangeSet)
                            {
                                this.HandleChangesetException(exception, this.batchOperationContexts, changesetBoundary, writer); 
                            }
                            else if (this.batchRequestStream.State == BatchStreamState.Post || 
                                     this.batchRequestStream.State == BatchStreamState.Put || 
                                     this.batchRequestStream.State == BatchStreamState.Delete ||
                                     this.batchRequestStream.State == BatchStreamState.Merge) 
                            {
                                // Store the exception if its in the middle of the changeset,
                                // we need to write the same exception for every
                                exceptionEncountered = exception; 
                            }
                            else 
                            { 
                                DataServiceHostWrapper currentHost = this.operationContext == null ? null : this.operationContext.Host;
                                if (currentHost == null) 
                                {
                                    // For error cases (like we encounter an error while parsing request headers
                                    // and were not able to create the host), we need to create a dummy host
                                    currentHost = new DataServiceHostWrapper(new BatchServiceHost(this.batchBoundary, writer)); 
                                }
 
                                ErrorHandler.HandleBatchProcessException(this, currentHost, exception, writer); 
                            }
                        } 
                        finally
                        {
                            // Once the end of the changeset is reached, clear the error state
                            if (this.batchRequestStream.State == BatchStreamState.EndChangeSet) 
                            {
                                exceptionEncountered = null; 
                                changesetBoundary = null; 
                                this.batchRequestDescription.Clear();
                                this.batchOperationContexts.Clear(); 
                            }
                        }
                    }
 
                    BatchWriter.WriteEndBoundary(writer, this.batchBoundary);
                    writer.Flush(); 
 
                    Exception ex = this.batchRequestStream.ValidateNoDataBeyondEndOfBatch();
                    if (ex != null) 
                    {
                        ErrorHandler.HandleBatchRequestException(this, ex, writer);
                        writer.Flush();
                    } 
                }
#if DEBUG 
                catch 
                {
                    // We've hit an error, some of the processing pipeline events will not get fired. Setting this flag to skip validation. 
                    this.ProcessingPipeline.SkipDebugAssert = true;
                    throw;
                }
#endif 
                finally
                { 
                    this.DisposeDataSource(); 
                }
            } 

            #region Private methods.

            ///  
            /// Creates an operation context for the current batch operation
            ///  
            /// Absolute service uri 
            /// batch stream which contains the header information.
            /// content ids that are defined in the batch. 
            /// Part separator for host.
            /// Output writer.
            /// instance of the operation context which represents the current operation.
            private static DataServiceOperationContext CreateOperationContextFromBatchStream(Uri absoluteServiceUri, BatchStream batchStream, HashSet contentIds, string boundary, StreamWriter writer) 
            {
                Debug.Assert(absoluteServiceUri != null && absoluteServiceUri.IsAbsoluteUri, "absoluteServiceUri != null && absoluteServiceUri.IsAbsoluteUri"); 
                Debug.Assert(batchStream != null, "batchStream != null"); 
                Debug.Assert(boundary != null, "boundary != null");
 
                // If the Content-ID header is defined, it should be unique.
                string contentIdValue;
                if (batchStream.ContentHeaders.TryGetValue(XmlConstants.HttpContentID, out contentIdValue) && !String.IsNullOrEmpty(contentIdValue))
                { 
                    int contentId;
                    if (!Int32.TryParse(contentIdValue, System.Globalization.NumberStyles.Integer, System.Globalization.NumberFormatInfo.InvariantInfo, out contentId)) 
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.DataService_ContentIdMustBeAnInteger(contentIdValue));
                    } 

                    if (!contentIds.Add(contentId))
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.DataService_ContentIdMustBeUniqueInBatch(contentId)); 
                    }
                } 
 
                BatchServiceHost host = new BatchServiceHost(absoluteServiceUri, batchStream, contentIdValue, boundary, writer);
                DataServiceOperationContext operationContext = new DataServiceOperationContext(true /*isBatchRequest*/, host); 
                operationContext.InitializeAndCacheHeaders();
                return operationContext;
            }
 
            /// 
            /// Write the exception encountered in the middle of the changeset to the response 
            ///  
            /// exception encountered
            /// list of operation contexts in the changeset 
            /// changeset boundary for the current processing changeset
            /// writer to which the response needs to be written
            private void HandleChangesetException(
                Exception exception, 
                List changesetOperationContexts,
                string changesetBoundary, 
                StreamWriter writer) 
            {
                Debug.Assert(exception != null, "exception != null"); 
                Debug.Assert(changesetOperationContexts != null, "changesetOperationContexts != null");
                Debug.Assert(WebUtil.IsCatchableExceptionType(exception), "WebUtil.IsCatchableExceptionType(exception)");

                // For a changeset, we need to write the exception only once. Since we ignore all the changesets 
                // after we encounter an error, its the last changeset which had error. For cases, which we don't
                // know, (like something in save changes, etc), we will still write the last operation information. 
                // If there are no host, then just pass null. 
                DataServiceHostWrapper currentHost = null;
                DataServiceOperationContext currentContext = changesetOperationContexts.Count == 0 ? null : changesetOperationContexts[changesetOperationContexts.Count - 1]; 
                if (currentContext == null || currentContext.Host == null)
                {
                    currentHost = new DataServiceHostWrapper(new BatchServiceHost(changesetBoundary, writer));
                } 
                else
                { 
                    currentHost = currentContext.Host; 
                }
 
                ErrorHandler.HandleBatchProcessException(this, currentHost, exception, writer);

                // Write end boundary for the changeset
                BatchWriter.WriteEndBoundary(writer, changesetBoundary); 
                this.Updatable.ClearChanges();
            } 
 
            /// Increases the count of batch changsets/queries found, and checks it is within limits.
            private void IncreaseBatchCount() 
            {
                checked
                {
                    this.batchElementCount++; 
                }
 
                if (this.batchElementCount > this.dataService.Configuration.MaxBatchCount) 
                {
                    this.batchLimitExceeded = true; 
                    throw new DataServiceException(400, Strings.DataService_BatchExceedMaxBatchCount(this.dataService.Configuration.MaxBatchCount));
                }
            }
 
            /// Increases the count of changeset CUD operations found, and checks it is within limits.
            private void IncreaseChangeSetCount() 
            { 
                checked
                { 
                    this.changeSetElementCount++;
                }

                if (this.changeSetElementCount > this.dataService.Configuration.MaxChangesetCount) 
                {
                    throw new DataServiceException(400, Strings.DataService_BatchExceedMaxChangeSetCount(this.dataService.Configuration.MaxChangesetCount)); 
                } 
            }
 
            /// 
            ///  For POST operations, the RequestEnumerable could be out of date
            ///  when a PUT is referring to the POST within the changeset.
            ///  We need to update the RequestEnumerable to reflect what actually 
            ///  happened to the database.
            ///  
            /// description for the current request. 
            private void UpdateRequestEnumerableForPut(RequestDescription requestDescription)
            { 
                Debug.Assert(this.batchRequestStream.State == BatchStreamState.Put, "This method must be called only for PUT requests");
                Debug.Assert(this.batchRequestDescription[this.batchRequestDescription.Count - 1] == requestDescription, "The current request description must be the last one");
                Debug.Assert(this.batchRequestDescription.Count == this.batchOperationContexts.Count, "Host and request description count must be the same");
 
                // If this PUT request is cross referencing some resource
                string identifier = requestDescription.SegmentInfos[0].Identifier; 
                if (identifier.StartsWith("$", StringComparison.Ordinal)) 
                {
                    // Get the content id of the POST request that is being cross-referenced 
                    string contentId = identifier.Substring(1);

                    // Now we need to scan all the previous request to find the
                    // POST request resource which is cross-referenced by the current request 
                    // and replace the resource in the POST request by the current one.
 
                    // Note: since today we do not return payloads in the PUT request, this is fine. 
                    // When we support that, we need to find all the PUT requests that also refers
                    // to the same resource and replace it with the current version. 

                    // Ignore the last one, since the parameters to the method are the last ones.
                    for (int i = 0; i < this.batchOperationContexts.Count - 1; i++)
                    { 
                        DataServiceOperationContext previousContext = this.batchOperationContexts[i];
                        BatchServiceHost previousHost = previousContext.Host.BatchServiceHost; 
                        RequestDescription previousRequest = this.batchRequestDescription[i]; 

                        if (previousContext.Host.AstoriaHttpVerb == AstoriaVerbs.POST && previousHost.ContentId == contentId) 
                        {
                            object resource = Deserializer.GetCrossReferencedResource(requestDescription.LastSegmentInfo);
                            previousRequest.LastSegmentInfo.RequestEnumerable = new object[] { resource };
                            break; 
                        }
                    } 
                } 
            }
 
            /// 
            /// Write the response for the given request, if required.
            /// 
            /// description of the request uri. If this is null, means that no response needs to be written 
            /// Batch host for which the request should be written.
            private void WriteRequest(RequestDescription description, BatchServiceHost batchHost) 
            { 
                Debug.Assert(batchHost != null, "host != null");
 
                // For DELETE operations, description will be null
                if (description == null)
                {
                    BatchWriter.WriteBoundaryAndHeaders(batchHost.Writer, batchHost, batchHost.ContentId, batchHost.BoundaryString); 
                }
                else 
                { 
                    Action responseWriter = DataService.SerializeResponseBody(description, this);
                    if (responseWriter != null) 
                    {
                        BatchWriter.WriteBoundaryAndHeaders(batchHost.Writer, batchHost, batchHost.ContentId, batchHost.BoundaryString);
                        batchHost.Writer.Flush();
                        responseWriter(batchHost.Writer.BaseStream); 
                        batchHost.Writer.WriteLine();
                    } 
                    else 
                    {
                        BatchWriter.WriteBoundaryAndHeaders(batchHost.Writer, batchHost, batchHost.ContentId, batchHost.BoundaryString); 
                    }
                }
            }
 
            #endregion Private methods.
        } 
 
        /// 
        /// For performance reasons we reuse results from existing query to read a projected value. We create an enumerator 
        /// containing the projected value but must not dispose the original query until later. This wrapper allows us to
        /// pass the created enumerator and dispose the query at the right time.
        /// 
        private class QueryResultsWrapper : IEnumerator, IDisposable 
        {
            ///  
            /// Query that needs to be disposed. 
            /// 
            private IEnumerator query; 

            /// 
            /// Enumerator containing the projected property.
            ///  
            private IEnumerator enumerator;
 
            ///  
            /// QueryResultsWrapper constructor
            ///  
            /// Enumerator containing the projected value.
            /// Query that needs to be disposed.
            public QueryResultsWrapper(IEnumerator enumerator, IEnumerator query)
            { 
                Debug.Assert(enumerator != null, "enumerator != null");
                this.enumerator = enumerator; 
                this.query = query; 
            }
 
            #region IEnumerator Members

            /// 
            /// Gets the current element from enumerator. 
            /// 
            object IEnumerator.Current 
            { 
                get { return this.enumerator.Current; }
            } 

            /// 
            /// Moves the enumerator to the next element.
            ///  
            /// true if the enumerator moved;false if the enumerator reached the end of the collection.
            bool IEnumerator.MoveNext() 
            { 
                return this.enumerator.MoveNext();
            } 

            /// 
            /// Resets the enumerator to the initial position.
            ///  
            void IEnumerator.Reset()
            { 
                this.enumerator.Reset(); 
            }
 
            #endregion

            #region IDisposable Members
            ///  
            /// Disposes the cached query.
            ///  
            void IDisposable.Dispose() 
            {
                WebUtil.Dispose(this.query); 
                GC.SuppressFinalize(this);
            }

            #endregion 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK