DataService.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataWeb / Server / System / Data / Services / DataService.cs / 1 / 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.Diagnostics; 
    using System.IO;
    using System.Linq; 
    using System.ServiceModel; 
    using System.ServiceModel.Activation;
    using System.ServiceModel.Channels; 
    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
    /// System.Data.Object.ObjectContext or another class that provides IQueryable
    /// 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; 

        /// Host implementation for this data service. 
        private IDataServiceHost host;

        /// Data provider for this data service.
        private IDataServiceProvider provider; 

        /// Cached request headers. 
        private CachedRequestParams requestParams; 

        ///  dummy data service for batch requests. 
        private BatchDataService batchDataService;

        #endregion Private fields.
 
        #region Properties.
 
        /// Service configuration information. 
        DataServiceConfiguration IDataService.Configuration
        { 
            [DebuggerStepThrough]
            get { return this.configuration; }
        }
 
        /// Host implementation for this data service
        IDataServiceHost IDataService.Host 
        { 
            [DebuggerStepThrough]
            get { return this.host; } 
        }

        /// Data provider for this data service
        IDataServiceProvider IDataService.Provider 
        {
            [DebuggerStepThrough] 
            get 
            {
                Debug.Assert(this.provider != null, "this.provider != null -- otherwise EnsureProviderAndConfigForRequest didn't"); 
                return this.provider;
            }
        }
 
        /// Returns the instance of data service.
        IDataService IDataService.Instance 
        { 
            [DebuggerStepThrough]
            get { return this; } 
        }

        /// Cached request headers.
        CachedRequestParams IDataService.RequestParams 
        {
            [DebuggerStepThrough] 
            get { return this.requestParams; } 
        }
 
        /// 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 (!WebUtil.IsCatchableExceptionType(handlingException))
                { 
                    throw; 
                }
 
                args.Exception = handlingException;
            }
        }
 
        /// 
        /// 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 (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)
        { 
            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.host = 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
            {
                this.EnsureProviderAndConfigForRequest();
                Action writer = this.HandleRequest(); 
                Debug.Assert(writer != null, "writer != null");
                Message result = CreateMessage(MessageVersion.None, "", ((IDataServiceHost)httpHost).ResponseContentType, writer, this); 
                shouldDispose = false; 
                return result;
            } 
            finally
            {
                if (shouldDispose)
                { 
                    ((IDataService)this).DisposeDataSource();
                } 
            } 
        }
 
        /// Provides a host-agnostic entry point for request processing.
        public void ProcessRequest()
        {
            if (this.host == null) 
            {
                throw new InvalidOperationException(Strings.DataService_HostNotAttached); 
            } 

            try 
            {
                this.EnsureProviderAndConfigForRequest();
                Action writer = this.HandleRequest();
                if (writer != null) 
                {
                    writer(this.host.ResponseStream); 
                } 
            }
            finally 
            {
                ((IDataService)this).DisposeDataSource();
            }
        } 

        #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"); 
        } 

        ///  
        /// 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. 
        }

        #endregion Protected methods.
 
        #region Private methods.
 
        /// Checks that the specified  has a known version. 
        /// Service to check.
        private static void CheckVersion(IDataService service) 
        {
            Debug.Assert(service != null, "service != null");
            Debug.Assert(service.RequestParams != null, "service.RequestParams != null");
 
            // Check that the request/payload version is understood.
            string versionText = service.RequestParams.Version; 
            if (!String.IsNullOrEmpty(versionText)) 
            {
                KeyValuePair version; 
                if (!HttpProcessUtility.TryReadVersion(versionText, out version))
                {
                    throw DataServiceException.CreateBadRequestError(
                        Strings.DataService_VersionCannotBeParsed(versionText)); 
                }
 
                // Currently we only recognize an exact match. In future versions 
                // we may choose to allow different major/minor combinations.
                if (version.Key.Major != XmlConstants.DataServiceVersionCurrentMajor || 
                    version.Key.Minor != XmlConstants.DataServiceVersionCurrentMinor)
                {
                    string message = Strings.DataService_VersionNotSupported(
                        version.Key.ToString(2), 
                        XmlConstants.DataServiceVersionCurrentMajor,
                        XmlConstants.DataServiceVersionCurrentMinor); 
                    throw DataServiceException.CreateBadRequestError(message); 
                }
            } 

            // Check that the maximum version for the client will understand our response.
            versionText = service.RequestParams.MaxVersion;
            if (!String.IsNullOrEmpty(versionText)) 
            {
                KeyValuePair version; 
                if (!HttpProcessUtility.TryReadVersion(versionText, out version)) 
                {
                    throw DataServiceException.CreateBadRequestError( 
                        Strings.DataService_VersionCannotBeParsed(versionText));
                }

                if (version.Key.Major < XmlConstants.DataServiceVersionCurrentMajor || 
                    (version.Key.Major == XmlConstants.DataServiceVersionCurrentMajor &&
                     version.Key.Minor < XmlConstants.DataServiceVersionCurrentMinor)) 
                { 
                    string message = Strings.DataService_VersionTooLow(
                        version.Key.ToString(2), 
                        XmlConstants.DataServiceVersionCurrentMajor,
                        XmlConstants.DataServiceVersionCurrentMinor);
                    throw DataServiceException.CreateBadRequestError(message);
                } 
            }
        } 
 
        /// 
        /// Checks that if etag values are specified in the header, they must be valid. 
        /// 
        /// header values.
        private static void CheckETagValues(CachedRequestParams requestParams)
        { 
            Debug.Assert(requestParams != null, "requestParams != null");
            if (!IsETagValueValid(requestParams.IfMatch)) 
            { 
                throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError);
            } 

            if (!IsETagValueValid(requestParams.IfNoneMatch))
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError); 
            }
        } 
 
        /// 
        /// Returns false if the given etag value is not valid. 
        /// Look in http://www.ietf.org/rfc/rfc2616.txt?number=2616 (Section 14.26) for more information
        /// 
        /// etag value to be checked.
        /// returns true if the etag value is valid, otherwise returns false. 
        private static bool IsETagValueValid(string etag)
        { 
            if (String.IsNullOrEmpty(etag) || etag == XmlConstants.HttpAnyETag) 
            {
                return true; 
            }

            if (etag.Length <= 4 || etag[0] != 'W' || etag[1] != '/' || etag[2] != '"' || etag[etag.Length - 1] != '"')
            { 
                return false;
            } 
 
            for (int i = 3; i < etag.Length - 1; i++)
            { 
                // Format of etag looks something like: W/"etag property values"
                // according to HTTP RFC 2616, if someone wants to specify more than 1 etag value,
                // then need to specify something like this: W/"etag values", W/"etag values", ...
                // To make sure only one etag is specified, we need to ensure that 
                // only the third and last characters are quotes.
                // If " is part of the key value, it needs to be escaped. 
                if (etag[i] == '"') 
                {
                    return false; 
                }
            }

            return true; 
        }
 
        ///  
        /// 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 configuration for the specified service.
        /// Type of DataService with authorization methods. 
        /// Provider with metadata.
        /// Instance of the data source for the provider.
        /// 
        /// A (possibly shared) configuration implementation for the specified service. 
        /// 
        private static DataServiceConfiguration CreateConfiguration(Type dataServiceType, IDataServiceProvider provider, object dataSourceInstance) 
        { 
            Debug.Assert(dataServiceType != null, "dataServiceType != null");
 
            DataServiceConfiguration result = new DataServiceConfiguration(provider);
            result.InvokeStaticInitialization(dataServiceType);
            result.RegisterCallbacks(dataServiceType);
            result.ApplyToProvider(dataSourceInstance); 
            return result;
        } 
 
        /// 
        /// Creates a provider implementation that wraps the T type. 
        /// 
        /// Type of DataService with service operations.
        /// Instance of the data source for the provider.
        /// Service configuration information. 
        /// 
        /// A (possibly shared) provider implementation that wraps the T type. 
        ///  
        private static IDataServiceProvider CreateProvider(Type dataServiceType, object dataSourceInstance, out DataServiceConfiguration configuration)
        { 
            Debug.Assert(dataServiceType != null, "dataServiceType != null");
            Debug.Assert(dataSourceInstance != null, "dataSourceInstance != null");

            Type dataContextType = typeof(T); 
            Debug.Assert(
                dataContextType.IsAssignableFrom(dataSourceInstance.GetType()), 
                "dataContextType.IsAssignableFrom(dataSourceInstance.GetType()) -- otherwise the wrong data source instance was created."); 

            MetadataCacheItem metadata = MetadataCache.TryLookup(dataServiceType, dataSourceInstance); 
            bool metadataRequiresInitialization = metadata == null;
            if (metadataRequiresInitialization)
            {
                metadata = new MetadataCacheItem(dataContextType); 
            }
 
            BaseServiceProvider result; 
            if (typeof(ObjectContext).IsAssignableFrom(dataContextType))
            { 
                result = new ObjectContextServiceProvider(metadata, dataSourceInstance);
            }
            else
            { 
                result = new ReflectionServiceProvider(metadata, dataSourceInstance);
            } 
 
            if (metadataRequiresInitialization)
            { 
                // Populate metadata in provider.
                result.PopulateMetadata();
                result.AddOperationsFromType(dataServiceType);
 
                // Create and cache configuration, which goes hand-in-hand with metadata.
                metadata.Configuration = CreateConfiguration(dataServiceType, result, dataSourceInstance); 
                metadata.Seal(); 

                MetadataCache.AddCacheItem(dataServiceType, dataSourceInstance, metadata); 
            }

            configuration = metadata.Configuration;
 
            return (IDataServiceProvider)result;
        } 
 
        /// 
        /// 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(IDataServiceHost 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. 
        /// Request parameters.
        private static void ValidateRequest(CachedRequestParams requestParams) 
        {
            if (!String.IsNullOrEmpty(requestParams.IfMatch) && !String.IsNullOrEmpty(requestParams.IfNoneMatch))
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_BothIfMatchAndIfNoneMatchHeaderSpecified); 
            }
        } 
 
        /// 
        /// 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.Host != null, "dataService.Host != null");
 
            CachedRequestParams requestParams = dataService.RequestParams;
            CheckVersion(dataService); 
            CheckETagValues(dataService.RequestParams); 

            ResourceContainer lastSegmentContainer = description.LastSegmentInfo.TargetContainer; 
            if (requestParams.AstoriaHttpVerb == AstoriaVerbs.GET)
            {
                if (lastSegmentContainer != null)
                { 
                    dataService.Configuration.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 (requestParams.AstoriaHttpVerb == AstoriaVerbs.POST)
                {
                    newDescription = HandlePostOperation(description, dataService); 
                    if (description.LinkUri)
                    { 
                        statusCode = 204;   // 204 - No Content 
                        shouldWriteBody = false;
                    } 
                    else
                    {
                        statusCode = 201;   // 201 - Created.
                    } 
                }
                else if (requestParams.AstoriaHttpVerb == AstoriaVerbs.PUT || 
                         requestParams.AstoriaHttpVerb == AstoriaVerbs.MERGE) 
                {
                    if (lastSegmentContainer != null) 
                    {
                        if (requestParams.AstoriaHttpVerb == AstoriaVerbs.PUT)
                        {
                            dataService.Configuration.CheckResourceRights(lastSegmentContainer, EntitySetRights.WriteReplace); 
                        }
                        else 
                        { 
                            dataService.Configuration.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);
                    statusCode = 204;   // 204 - No Content 
                } 
                else if (requestParams.AstoriaHttpVerb == AstoriaVerbs.DELETE)
                { 
                    if (lastSegmentContainer != null)
                    {
                        dataService.Configuration.CheckResourceRights(lastSegmentContainer, EntitySetRights.WriteDelete);
                    } 

                    HandleDeleteOperation(description, dataService); 
                    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.
            dataService.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.
            dataService.Host.ResponseVersion = XmlConstants.DataServiceVersionCurrent; 

            dataService.Host.ResponseStatusCode = statusCode; 
 
            if (shouldWriteBody)
            { 
                dataService.Host.ResponseVersion = XmlConstants.DataServiceVersionCurrent;

                // 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.Host != null, "dataService.Host != null");
 
            CachedRequestParams requestParams = dataService.RequestParams;
 
            // 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 (!description.IsSingleResult || (description.ExpandPaths != null && description.ExpandPaths.Count != 0)) 
            { 
                if (!String.IsNullOrEmpty(requestParams.IfMatch) || !String.IsNullOrEmpty(requestParams.IfNoneMatch))
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagSpecifiedForCollection(requestParams.AbsoluteRequestUri));
                }
            }
 
            if (requestParams.AstoriaHttpVerb == AstoriaVerbs.PUT ||
                requestParams.AstoriaHttpVerb == AstoriaVerbs.MERGE) 
            { 
                ResourceContainer container;
                object actualEntity = GetContainerAndActualEntityInstance(dataService.Provider, description, out container); 
                dataService.Host.ResponseETag = WebUtil.GetETagValue(dataService.Provider, actualEntity, container);
                return EmptyStreamWriter;
            }
 
            // 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.TargetElementType, 
                requestParams.Accept,
                description.MimeType,
                dataService.Host);
 
            // check for etags first
            // If no etag is specified, then do the normal stuff - run the query and serialize the result 
            if (description.TargetSource == RequestTargetSource.ServiceOperation || 
                description.TargetSource == RequestTargetSource.None ||
                !description.IsSingleResult) 
            {
                Debug.Assert(
                    String.IsNullOrEmpty(requestParams.IfMatch) && String.IsNullOrEmpty(requestParams.IfNoneMatch),
                    "No etag can be specified for collection"); 
                Encoding encoding = GetRequestAcceptEncoding(responseFormat, requestParams.AcceptCharset);
                IEnumerator queryResults = WebUtil.GetRequestEnumerator(description.RequestEnumerable); 
                try 
                {
                    bool hasMoved = queryResults.MoveNext(); 

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

                    dataService.Host.ResponseContentType = HttpProcessUtility.BuildContentType(dataService.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.
        /// CLR element type.
        /// Accept header value.
        /// Required MIME type. 
        /// Host implementation for this data service.
        ///  
        /// The content format for the resource; Unknown if it cannot be determined statically. 
        /// 
        private static ContentFormat SelectResponseFormatForType( 
            RequestTargetKind targetKind,
            Type elementType,
            string acceptTypesText,
            string mimeType, 
            IDataServiceHost host)
        { 
            ContentFormat responseFormat; 
            if (targetKind == RequestTargetKind.PrimitiveValue)
            { 
                responseFormat = SelectPrimitiveContentType(elementType, acceptTypesText, mimeType, host);
            }
#if ASTORIA_OPEN_OBJECT
            else if (targetKind != RequestTargetKind.OpenPropertyValue && targetKind != RequestTargetKind.OpenProperty) 
#else
            else 
#endif 
            {
                bool entityTarget = targetKind == RequestTargetKind.Resource; 
                responseFormat = SelectResponseFormat(host, acceptTypesText, entityTarget);
                if (responseFormat == ContentFormat.Unsupported)
                {
                    throw new DataServiceException(415, Strings.DataServiceException_UnsupportedMediaType); 
                }
            } 
#if ASTORIA_OPEN_OBJECT 
            else
            { 
                // We cannot negotiate a format until we know what the value is for the object.
                responseFormat = ContentFormat.Unknown;
            }
#endif 

            return responseFormat; 
        } 

        /// Selects the correct content format for a primitive type. 
        /// CLR element 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(Type targetElementType, string acceptTypesText, string requiredContentType, IDataServiceHost host) 
        { 
            Debug.Assert(targetElementType != null, "targetElementType != null");
 
            string contentType;
            ContentFormat responseFormat = WebUtil.GetResponseFormatForPrimitiveValue(targetElementType, out contentType);
            requiredContentType = requiredContentType ?? contentType;
            host.ResponseContentType = HttpProcessUtility.SelectRequiredMimeType( 
                acceptTypesText,        // acceptTypesText
                new string[] { requiredContentType },    // exactContentType 
                requiredContentType);   // inexactContentType 
            return responseFormat;
        } 

        /// 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");

            CachedRequestParams requestParams = dataService.RequestParams;
            if (!String.IsNullOrEmpty(requestParams.IfMatch) || !String.IsNullOrEmpty(requestParams.IfNoneMatch)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagSpecifiedForPost); 
            } 

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

            Stream requestStream = dataService.Host.RequestStream; 
            if (requestStream == null) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_NullRequestStream); 
            }

            string mimeType;
            System.Text.Encoding encoding; 
            HttpProcessUtility.ReadContentType(dataService.Host.RequestContentType, out mimeType, out encoding);
            ContentFormat requestFormat = WebUtil.SelectRequestFormat(mimeType, description); 
 
            object entity = null;
            Deserializer deserializer = null; 
            try
            {
                switch (requestFormat)
                { 
                    case ContentFormat.Json:
                        StreamReader streamReader = new StreamReader(requestStream, encoding); 
                        deserializer = new JsonDeserializer( 
                            streamReader,
                            false /*update*/, 
                            dataService,
                            UpdateTracker.CreateUpdateTracker(description, dataService.Provider));
                        break;
                    case ContentFormat.Atom: 
                        SyndicationFormatterFactory factory = new Atom10FormatterFactory();
                        deserializer = new SyndicationDeserializer( 
                            requestStream,  // stream 
                            encoding,       // encoding
                            dataService,    // dataService 
                            false,          // update
                            factory,
                            UpdateTracker.CreateUpdateTracker(description, dataService.Provider));       // factory
                        break; 
                    case ContentFormat.PlainXml:
                        deserializer = new PlainXmlDeserializer( 
                            requestStream, 
                            encoding,
                            dataService, 
                            false /*update*/,
                            UpdateTracker.CreateUpdateTracker(description, dataService.Provider));
                        break;
                    default: 
                        throw new DataServiceException(415, Strings.BadRequest_UnsupportedMediaForPost(mimeType));
                } 
 
                Debug.Assert(deserializer != null, "deserializer != null");
                entity = deserializer.HandlePostRequest(description); 
                Debug.Assert(entity != null, "entity != null");

                if (deserializer.Tracker != null)
                { 
                    deserializer.Tracker.FireNotifications(dataService.Instance);
                } 
 
                return RequestDescription.CreateSingleResultRequestDescription(
                        description, entity, description.LastSegmentInfo.TargetContainer); 
            }
            finally
            {
                WebUtil.Dispose(deserializer); 
            }
        } 
 
        /// 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");
 
            if (!description.IsSingleResult) 
            {
                throw DataServiceException.CreateMethodNotAllowed( 
                    Strings.BadRequest_InvalidUriForPutOperation(dataService.RequestParams.AbsoluteRequestUri),
                    dataService.Configuration.GetAllowedMethods(description));
            }
 
            if (!String.IsNullOrEmpty(dataService.RequestParams.IfNoneMatch))
            { 
                throw DataServiceException.CreateBadRequestError(Strings.DataService_IfNoneMatchHeaderNotSupportedInPut); 
            }
            else if (description.LinkUri && !String.IsNullOrEmpty(dataService.RequestParams.IfMatch)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagsNotAllowedForLinkOperations);
            }
            else if (description.Property != null && description.Property.IsOfKind(ResourcePropertyKind.Key)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotUpdateKeyProperties(description.Property.Name)); 
            } 

            Stream requestStream = dataService.Host.RequestStream; 
            if (requestStream == null)
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_NullRequestStream);
            } 

            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.RequestParams != null, "dataService.RequestParams != null"); 

            // 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.LinkUri)
            { 
                HandleUnbindOperation(description, dataService); 
            }
            else if ( 
#if ASTORIA_OPEN_OBJECT
                (description.TargetKind == RequestTargetKind.OpenProperty) ||
#endif
                (description.IsSingleResult && description.TargetKind == RequestTargetKind.Resource)) 
            {
#if ASTORIA_OPEN_OBJECT 
                Debug.Assert( 
                    description.LastSegmentInfo.TargetContainer != null || description.TargetKind == RequestTargetKind.OpenProperty,
                    "description.LastSegmentInfo.TargetContainer != null || TargetKind == OpenProperty"); 

#endif

                if (description.RequestEnumerable == null) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation); 
                } 

                // 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*/);
                ResourceContainer container = description.LastSegmentInfo.TargetContainer;
 
                //
 
                object actualEntity = dataService.Provider.ResolveResource(entity); 

                // For open properties, we need to make sure that they refer to resource types 
#if ASTORIA_OPEN_OBJECT
                if (description.TargetKind == RequestTargetKind.OpenProperty)
                {
                    // Verify that the resource is an entity type. Otherwise we need to throw 
                    ResourceType resourceType = dataService.Provider.GetResourceType(actualEntity.GetType());
                    if (resourceType == null || resourceType.ResourceTypeKind != ResourceTypeKind.EntityType) 
                    { 
                        throw DataServiceException.CreateBadRequestError(
                            Strings.DataService_TypeNotValidForDeleteOperation(dataService.RequestParams.AbsoluteRequestUri)); 
                    }

                    container = dataService.Provider.GetContainerForResourceType(resourceType.Type);
                } 
#endif
 
                if (description.Property != null) 
                {
                    Debug.Assert(container != null, "container != null"); 
                    dataService.Configuration.CheckResourceRights(container, EntitySetRights.WriteDelete);
                }

                CheckForETagInDeleteOperation(actualEntity, entity, container, dataService.RequestParams, dataService.Provider); 
                dataService.Provider.DeleteResource(entity);
 
                // 
#if ASTORIA_OPEN_OBJECT
                if (description.TargetKind != RequestTargetKind.OpenProperty) 
#endif
                {
                    UpdateTracker.FireNotification(dataService.Instance, 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 (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'). 
                ResourceContainer container;    // Resource Container to which the parent entity belongs to.
                object resource = Deserializer.GetResourceToModify(description, dataService, false /*allowCrossReference*/, out securityResource, out container); 
 
                //
 
                object actualEntity = dataService.Provider.ResolveResource(securityResource);
                CheckForETagInDeleteOperation(actualEntity, securityResource, container, dataService.RequestParams, dataService.Provider);

                // 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.Provider);
                UpdateTracker.FireNotification(dataService.Instance, actualEntity, container, UpdateOperations.Change); 
            } 
#if ASTORIA_OPEN_OBJECT
            else if (description.TargetKind == RequestTargetKind.OpenPropertyValue) 
            {
                object securityResource;
                object resource = Deserializer.GetResourceToModify(description, dataService, out securityResource);
 
                //
 
                object actualEntity = dataService.Provider.ResolveResource(resource); 
                ResourceContainer container = dataService.Provider.GetContainerForResourceType(actualEntity.GetType());
                CheckForETagInDeleteOperation(actualEntity, resource, container, dataService.RequestParams, dataService.Provider); 

                // 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.Provider);
            } 
#endif
            else 
            { 
                throw DataServiceException.CreateMethodNotAllowed(
                    Strings.BadRequest_InvalidUriForDeleteOperation(dataService.RequestParams.AbsoluteRequestUri), 
                    dataService.Configuration.GetAllowedMethods(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; 
            if (description.TargetKind == RequestTargetKind.Metadata) 
            {
                exactContentType = new string[] { XmlConstants.MimeMetadata }; 
                format = ContentFormat.MetadataDocument;
                mime = HttpProcessUtility.SelectRequiredMimeType(
                    dataService.RequestParams.Accept,   // acceptTypesText
                    exactContentType,                   // exactContentType 
                    XmlConstants.MimeApplicationXml);   // inexactContentType
            } 
            else if (description.TargetKind == RequestTargetKind.ServiceDirectory) 
            {
                exactContentType = new string[] { XmlConstants.MimeApplicationAtomService, XmlConstants.MimeApplicationJson, XmlConstants.MimeApplicationXml }; 
                mime = HttpProcessUtility.SelectRequiredMimeType(
                    dataService.RequestParams.Accept,   // 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(dataService.RequestParams.AcceptCharset); 
                dataService.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"); 
            CachedRequestParams requestParams = dataService.RequestParams;
            Debug.Assert( 
                String.IsNullOrEmpty(requestParams.IfMatch) || String.IsNullOrEmpty(requestParams.IfNoneMatch), 
                "Both If-Match and If-None-Match header cannot be specified");
            IEnumerator queryResults = null; 
            try
            {
                if (requestParams.AstoriaHttpVerb == AstoriaVerbs.GET)
                { 
                    bool writeResponse = true;
 
                    // Get the index of the last resource in the request uri 
                    int parentResourceIndex = description.GetIndexOfTargetEntityResource();
                    SegmentInfo parentEntitySegment = description.SegmentInfos[parentResourceIndex]; 
                    queryResults = RequestDescription.GetSingleResultFromEnumerable(parentEntitySegment);
                    object resource = queryResults.Current;
                    string etagValue = null;
 
                    if (description.LinkUri)
                    { 
                        // No need to worry about etags while performing link operations 
                        // No etags can be specified also while performing link operations
                        if (requestParams.IfMatch != null || requestParams.IfNoneMatch != null) 
                        {
                            throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagsNotAllowedForLinkOperations);
                        }
 
                        if (resource == null)
                        { 
                            throw DataServiceException.CreateResourceNotFound(description.LastSegmentInfo.Identifier); 
                        }
                    } 
                    else
                    {
                        ResourceContainer container = null;
                        if (resource != null) 
                        {
                            container = WebUtil.GetResourceContainer(resource, parentEntitySegment, dataService.Provider); 
                        } 

                        etagValue = WebUtil.CompareAndGetETag( 
                            resource, resource, container, dataService.Provider, requestParams, out writeResponse);

                        if (resource == null && description.TargetKind == RequestTargetKind.Resource)
                        { 
                            Debug.Assert(description.Property != null, "non-open type property");
 
                            // If you are querying reference nav property and the value is null, 
                            // return 204 - No Content e.g. /Customers(1)/BestFriend
                            dataService.Host.ResponseStatusCode = 204; // No Content 
                            return EmptyStreamWriter;
                        }

                        WriteETagValueInResponseHeader(description, etagValue, dataService.Host); 
                    }
 
                    if (writeResponse) 
                    {
                        int lastResourceIndex = description.GetIndexOfTargetEntityResource(); 
                        return WriteSingleElementResponse(description, responseFormat, queryResults, lastResourceIndex, etagValue, dataService);
                    }
                    else
                    { 
                        dataService.Host.ResponseStatusCode = 304; // Not Modified
                        return EmptyStreamWriter; 
                    } 
                }
                else 
                {
                    Debug.Assert(requestParams.AstoriaHttpVerb == AstoriaVerbs.POST, "Must be POST method");
                    ResourceContainer container;
                    object actualEntity = GetContainerAndActualEntityInstance(dataService.Provider, description, out container); 
                    dataService.Host.ResponseLocation = Serializer.GetUri(actualEntity, dataService.Provider, container, requestParams.AbsoluteServiceUri).AbsoluteUri;
                    string etagValue = WebUtil.GetETagValue(dataService.Provider, actualEntity, container); 
                    queryResults = RequestDescription.GetSingleResultFromEnumerable(description.LastSegmentInfo); 
                    return WriteSingleElementResponse(description, responseFormat, queryResults, description.SegmentInfos.Length - 1, etagValue, dataService);
                } 
            }
            catch
            {
                WebUtil.Dispose(queryResults); 
                throw;
            } 
        } 

#if ASTORIA_OPEN_OBJECT 

        /// 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 + " is open property or open property value");
            WebUtil.CheckResourceExists(element != null, description.LastSegmentInfo.Identifier); 
            Type elementType = element.GetType();
            ResourceType resourceType = dataService.Provider.GetResourceType(elementType); 
 
            // This resource wouldn't be visible during serialization, so we treat is as 404.
            if (resourceType == null) 
            {
                throw new InvalidOperationException(Strings.DataService_InvalidResourceType(elementType.FullName));
            }
 
            // Determine the appropriate target type based on the kind of resource.
            bool rawValue = description.TargetKind == RequestTargetKind.OpenPropertyValue; 
            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) 
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ValuesCanBeReturnedForPrimitiveTypesOnly); 
                    }
                    else
                    {
                        targetKind = RequestTargetKind.Resource; 
                    }
 
                    break; 
            }
 
            if (description.LinkUri)
            {
                targetKind = RequestTargetKind.Link;
            } 

            return SelectResponseFormatForType(targetKind, elementType, dataService.RequestParams.Accept, null, dataService.Host); 
        } 

#endif 

        /// 
        /// 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
            {
                if (parentResourceIndex != description.SegmentInfos.Length - 1) 
                {
                    // Dispose the old enumerator 
                    WebUtil.Dispose(queryResults); 

                    // get the resource which need to be written 
                    queryResults = RequestDescription.GetSingleResultFromEnumerable(description.LastSegmentInfo);
                }

                // If we had to wait until we got a value to determine the valid contents, try that now. 
#if ASTORIA_OPEN_OBJECT
                if (responseFormat == ContentFormat.Unknown) 
                { 
                    responseFormat = ResolveUnknownFormat(description, queryResults.Current, dataService);
                } 
#else
                Debug.Assert(responseFormat != ContentFormat.Unknown, "responseFormat != ContentFormat.Unknown");
#endif
 
                // Write the etag header
                WriteETagValueInResponseHeader(description, etagValue, dataService.Host); 
 
                Encoding encoding = GetRequestAcceptEncoding(responseFormat, dataService.RequestParams.AcceptCharset);
                dataService.Host.ResponseContentType = HttpProcessUtility.BuildContentType(dataService.Host.ResponseContentType, encoding); 
                return new ResponseBodyWriter(
                    encoding,
                    true /* hasMoved */,
                    dataService, 
                    queryResults,
                    description, 
                    responseFormat).Write; 
            }
            catch 
            {
                WebUtil.Dispose(queryResults);
                throw;
            } 
        }
 
        ///  
        /// Write the etag header value in the response
        ///  
        /// description about the request made
        /// etag value that needs to be written.
        /// Host implementation for this data service.
        private static void WriteETagValueInResponseHeader(RequestDescription requestDescription, string etagValue, IDataServiceHost host) 
        {
            Debug.Assert(requestDescription.IsSingleResult, "requestDescription.IsSingleResult"); 
            if ((requestDescription.ExpandPaths == null || requestDescription.ExpandPaths.Count == 0) 
                && !String.IsNullOrEmpty(etagValue))
            { 
                host.ResponseETag = etagValue;
            }
        }
 
        /// 
        /// Returns the actual entity instance and its containers for the resource in the description results. 
        ///  
        /// Data provider 
        /// 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(
            IDataServiceProvider provider, RequestDescription description, out ResourceContainer 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] = provider.ResolveResource(results[0]);
            container = description.LastSegmentInfo.TargetContainer; 
#if ASTORIA_OPEN_OBJECT
            if (container == null) 
            { 
                // Open types will not have TargetContainer set, but they don't support MEST either.
                Debug.Assert( 
                    RequestTargetKind.OpenProperty == description.LastSegmentInfo.TargetKind,
                    "RequestTargetKind.OpenProperty == description.LastSegmentInfo.TargetKind(" + description.LastSegmentInfo.TargetKind + " - otherwise, why is TargetContainer null for POST target?");
                container = provider.GetContainerForResourceType(results[0].GetType());
                Debug.Assert( 
                    container != null,
                    "container != null -- otherwise results[0].GetType() (" + results[0].GetType() + ") didn't work."); 
            } 
#else
            Debug.Assert(container != null, "description.LastSegmentInfo.TargetContainer != null"); 
#endif

            return results[0];
        } 

        ///  
        /// Check for etag values for the given resource in DeleteOperation 
        /// 
        /// resource whose etag value needs to be compared to the one given in the request header 
        /// token as returned by the IUpdatable.GetResource method.
        /// resource container to which the resource belongs to.
        /// request headers
        /// Data provider  
        private static void CheckForETagInDeleteOperation(
            object actualEntityInstance, 
            object entityToken, 
            ResourceContainer container,
            CachedRequestParams requestParams, 
            IDataServiceProvider provider)
        {
            Debug.Assert(actualEntityInstance != null, "actualEntityInstance != null");
            Debug.Assert(entityToken != null, "entityToken != null"); 

            // If this method is called for Update, we need to pass the token object as well as the actual instance. 
            // The actual instance is used to determine the type that's necessary to find out the etag properties. 
            // The token is required to pass back to IUpdatable interface, if we need to get the values for etag properties.
            if (!String.IsNullOrEmpty(requestParams.IfNoneMatch)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_IfNoneMatchHeaderNotSupportedInDelete);
            }
 
            ICollection etagProperties = provider.GetETagProperties(container.Name, actualEntityInstance.GetType());
            if (etagProperties.Count == 0) 
            { 
                if (requestParams.IfMatch != null)
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.Serializer_NoETagPropertiesForType);
                }
            }
            else if (String.IsNullOrEmpty(requestParams.IfMatch)) 
            {
                string typeName = WebUtil.GetTypeName(provider, actualEntityInstance.GetType()); 
                throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotPerformDeleteOperationWithoutETag(typeName)); 
            }
            else if (requestParams.IfMatch != XmlConstants.HttpAnyETag) 
            {
                string etagValue = WebUtil.GetETagValue(entityToken, etagProperties, provider);
                if (etagValue != requestParams.IfMatch)
                { 
                    throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch);
                } 
            } 
        }
 
        /// No-op method for a stream-writing action.
        /// Stream to write to.
        private static void EmptyStreamWriter(Stream stream)
        { 
        }
 
        ///  
        /// 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"); 
 
            object parentEntity;
            Deserializer.GetResourceToModify(description, dataService, out parentEntity); 
            if (description.Property != null)
            {
                if (description.Property.Kind == ResourcePropertyKind.ResourceReference)
                { 
                    dataService.Provider.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.Provider.RemoveReferenceFromCollection(parentEntity, description.Property.Name, childEntity);
                } 
            }
            else 
            { 
                if (description.LastSegmentInfo.HasKeyValues)
                { 
                    object childEntity = Deserializer.GetResource(description.LastSegmentInfo, null, dataService, true /*checkForNull*/);
                    dataService.Provider.RemoveReferenceFromCollection(parentEntity, description.ContainerName, childEntity);
                }
                else 
                {
                    dataService.Provider.SetReference(parentEntity, description.ContainerName, null); 
                } 
            }
        } 

        /// 
        /// 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 (mime == XmlConstants.MimeApplicationJson) 
            {
                return ContentFormat.Json;
            }
            else if (mime == XmlConstants.MimeApplicationAtom) 
            {
                return ContentFormat.Atom; 
            } 
            else
            { 
                Debug.Assert(
                    mime == XmlConstants.MimeApplicationXml || mime == XmlConstants.MimeTextXml,
                    "expecting application/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.host != null, "this.host != null");
            Action writer; 
            try 
            {
                if (this.host is HttpContextServiceHost) 
                {
                    ((HttpContextServiceHost)this.host).VerifyQueryParameters();
                }
 
                RequestDescription description = this.ProcessIncomingRequestUriAndCacheHeaders();
                this.OnStartProcessingRequest(new ProcessRequestArgs(this.requestParams.AbsoluteRequestUri, false /*isBatchOperation*/)); 
                if (description.TargetKind != RequestTargetKind.Batch) 
                {
                    writer = this.HandleNonBatchRequest(description); 
                }
                else
                {
                    writer = this.HandleBatchRequest(); 
                }
            } 
            catch (Exception exception) 
            {
                // Exception should be re-thrown if not handled. 
                if (!WebUtil.IsCatchableExceptionType(exception))
                {
                    throw;
                } 

                string accept = (this.requestParams != null) ? this.requestParams.Accept : null; 
                string acceptCharset = (this.requestParams != null) ? this.requestParams.AcceptCharset : 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"); 
            description = ProcessIncomingRequest(description, this);
 
            if (this.requestParams.AstoriaHttpVerb != AstoriaVerbs.GET) 
            {
                this.provider.SaveChanges(); 
            }

            return (description == null) ? EmptyStreamWriter : SerializeResponseBody(description, this);
        } 

        /// Handle the batch request. 
        /// Returns the delegate which takes the response stream for writing the response. 
        private Action HandleBatchRequest()
        { 
            // Verify the HTTP method.
            if (this.requestParams.AstoriaHttpVerb != AstoriaVerbs.POST)
            {
                throw DataServiceException.CreateMethodNotAllowed( 
                    Strings.DataService_BatchResourceOnlySupportsPost,
                    XmlConstants.HttpMethodPost); 
            } 

            CheckVersion(this); 

            // Verify the content type and get the boundary string
            Encoding encoding;
            string boundary; 

            if (!BatchStream.GetBoundaryAndEncodingFromMultipartMixedContentType(this.requestParams.ContentType, out boundary, out encoding) || 
                String.IsNullOrEmpty(boundary)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_InvalidContentTypeForBatchRequest); 
            }

            // Write the response headers
            this.host.ResponseStatusCode = 202; // OK 
            this.host.ResponseCacheControl = XmlConstants.HttpCacheControlNoCache;
 
            string batchBoundary = XmlConstants.HttpMultipartBoundaryBatchResponse + '_' + Guid.NewGuid().ToString(); 
            this.host.ResponseContentType = String.Format(
                System.Globalization.CultureInfo.InvariantCulture, 
                "{0}; {1}={2}",
                XmlConstants.MimeMultiPartMixed,
                XmlConstants.HttpMultipartBoundary,
                batchBoundary); 

            BatchStream batchStream = new BatchStream(this.host.RequestStream, boundary, encoding, true); 
            this.batchDataService = new BatchDataService(this, batchStream, batchBoundary); 
            return this.batchDataService.HandleBatchContent;
        } 

        /// Creates the provider and configuration as necessary to be used for this request.
        private void EnsureProviderAndConfigForRequest()
        { 
            if (this.provider == null)
            { 
                Type dataServiceType = this.GetType(); 
                object dataSourceInstance = this.CreateDataSource();
                if (dataSourceInstance == null) 
                {
                    throw new InvalidOperationException(Strings.DataService_CreateDataSourceNull);
                }
 
                this.provider = CreateProvider(dataServiceType, dataSourceInstance, out this.configuration);
            } 
            else 
            {
                Debug.Assert(this.configuration != null, "this.configuration != null -- otherwise this.provider was ----signed with no configuration"); 
            }
        }

        ///  
        /// Processes the incoming request and cache all the request headers
        ///  
        /// description about the request uri. 
        private RequestDescription ProcessIncomingRequestUriAndCacheHeaders()
        { 
            this.requestParams = new CachedRequestParams(
                this.host.RequestAccept,
                this.host.RequestAcceptCharSet,
                this.host.RequestContentType, 
                this.host.RequestHttpMethod,
                this.host.RequestIfMatch, 
                this.host.RequestIfNoneMatch, 
                this.host.RequestVersion,
                this.host.RequestMaxVersion, 
                RequestUriProcessor.GetAbsoluteRequestUri(this.host),
                RequestUriProcessor.GetServiceUri(this.host));

            ValidateRequest(this.requestParams); 
            return RequestUriProcessor.ProcessRequestUri(this.requestParams.AbsoluteRequestUri, this);
        } 
 
        #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 batchRequestHost = new List(); 
 
            /// Number of CUD operations encountered in the current changeset.
            private int changeSetElementCount; 

            /// Batch Host which caches the request headers and response headers per operation within a changeset.
            private IDataServiceHost host;
 
            #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.
            DataServiceConfiguration IDataService.Configuration 
            {
                get { return this.dataService.Configuration; }
            }
 
            /// Host implementation for the batch data service.
            IDataServiceHost IDataService.Host 
            { 
                get { return this.host; }
            } 

            /// Data provider for this data service.
            IDataServiceProvider IDataService.Provider
            { 
                get { return this.dataService.Provider; }
            } 
 
            /// Instance of the data provider.
            IDataService IDataService.Instance 
            {
                get { return this.dataService.Instance; }
            }
 
            /// Gets the cached request headers.
            CachedRequestParams IDataService.RequestParams 
            { 
                get { return ((BatchServiceHost)this.host).RequestParams; }
            } 

            /// 
            /// 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)
            { 
                this.dataService.InternalApplyingExpansions(queryable, expandPaths);
            }

            /// Processes a catchable exception. 
            /// The arguments describing how to handle the exception.
            void IDataService.InternalHandleException(HandleExceptionArgs args) 
            { 
                this.dataService.InternalHandleException(args);
            } 

            /// 
            /// 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) 
            {
                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. 
            object IDataService.GetResource(RequestDescription description, int segmentIndex, string typeFullName)
            { 
                if (description.SegmentInfos[0].Identifier.StartsWith("$", StringComparison.Ordinal))
                {
                    Debug.Assert(segmentIndex >= 0 && segmentIndex < description.SegmentInfos.Length, "segment index must be a valid one");
                    if (description.SegmentInfos[segmentIndex].RequestEnumerable == null) 
                    {
                        object resource = GetResourceFromSegmentEnumerable(description.SegmentInfos[0]); 
                        for (int i = 1; i <= segmentIndex; i++) 
                        {
                            resource = ((IDataService)this).Provider.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 GetResourceFromSegmentEnumerable(description.SegmentInfos[segmentIndex]); 
                    }
                } 
 
                return Deserializer.GetResource(description.SegmentInfos[segmentIndex], typeFullName, ((IDataService)this), false /*checkForNull*/);
            } 

            /// 
            /// Dispose the data source instance
            ///  
            void IDataService.DisposeDataSource()
            { 
                this.dataService.DisposeDataSource(); 
            }
 
            /// 
            /// This method is called before a request is processed.
            /// 
            /// Information about the request that is going to be processed. 
            void IDataService.InternalOnStartProcessingRequest(ProcessRequestArgs args)
            { 
                this.dataService.InternalOnStartProcessingRequest(args); 
            }
 
            #endregion

            /// 
            /// Handle the batch content 
            /// 
            /// response stream for writing batch response 
            internal void HandleBatchContent(Stream responseStream) 
            {
                BatchServiceHost batchHost = null; 
                RequestDescription description;
                string changesetBoundary = null;
                Exception exceptionEncountered = null;
 
                try
                { 
                    StreamWriter writer = new StreamWriter(responseStream, HttpProcessUtility.FallbackEncoding); 
                    while (!this.batchLimitExceeded && this.batchRequestStream.State != BatchStreamState.EndBatch)
                    { 
                        // clear the host from the last operation
                        this.host = 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);
                                    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)
                                    { 
                                        Debug.Assert(!String.IsNullOrEmpty(changesetBoundary), "!String.IsNullOrEmpty(changesetBoundary)");
 
                                        // Save all the changes and write the response 
                                        this.dataService.Provider.SaveChanges();
 
                                        Debug.Assert(this.batchRequestHost.Count == this.batchRequestDescription.Count, "counts must be the same");
                                        for (int i = 0; i < this.batchRequestDescription.Count; i++)
                                        {
                                            this.host = this.batchRequestHost[i]; 
                                            this.WriteRequest(this.batchRequestDescription[i], this.batchRequestHost[i]);
                                        } 
 
                                        BatchWriter.WriteEndBoundary(writer, changesetBoundary);
                                    } 
                                    else
                                    {
                                        this.HandleChangesetException(exceptionEncountered, this.batchRequestHost, changesetBoundary, writer);
                                    } 

                                    break; 
                                    #endregion //EndChangeSet 
                                case BatchStreamState.Get:
                                    #region GET Operation 
                                    this.IncreaseBatchCount();
                                    batchHost = CreateHostFromHeaders(
                                        this.dataService.Host,
                                        this.batchRequestStream, 
                                        this.contentIds,
                                        this.batchBoundary, 
                                        writer); 
                                    this.host = batchHost;
 
                                    // it must be GET operation
                                    Debug.Assert(this.host.RequestHttpMethod == XmlConstants.HttpMethodGet, "this.host.RequestHttpMethod == XmlConstants.HttpMethodGet");
                                    Debug.Assert(this.batchRequestDescription.Count == 0, "this.batchRequestDescription.Count == 0");
                                    Debug.Assert(this.batchRequestHost.Count == 0, "this.batchRequestHost.Count == 0"); 

                                    this.dataService.InternalOnStartProcessingRequest(new ProcessRequestArgs(this.host.AbsoluteRequestUri, true /*isBatchOperation*/)); 
                                    description = RequestUriProcessor.ProcessRequestUri(this.host.AbsoluteRequestUri, this); 
                                    description = ProcessIncomingRequest(description, this);
                                    this.WriteRequest(description, batchHost); 
                                    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();
                                    batchHost = CreateHostFromHeaders(this.dataService.Host, this.batchRequestStream, this.contentIds, changesetBoundary, writer);
                                    if (exceptionEncountered == null)
                                    { 
                                        this.batchRequestHost.Add(batchHost);
                                        this.host = batchHost; 
 
                                        this.dataService.InternalOnStartProcessingRequest(new ProcessRequestArgs(this.host.AbsoluteRequestUri, true /*isBatchOperation*/));
                                        description = RequestUriProcessor.ProcessRequestUri(this.host.AbsoluteRequestUri, this); 
                                        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 (this.batchRequestStream.State == BatchStreamState.Post && description != null)
                                        { 
                                            Debug.Assert(description.TargetKind == RequestTargetKind.Resource, "The target must be a resource, since otherwise cross-referencing doesn't make sense"); 

                                            // if the content id is specified, only then add it to the collection 
                                            if (batchHost.ContentId != null)
                                            {
                                                this.contentIdsToSegmentInfoMapping.Add(batchHost.ContentId, description.LastSegmentInfo);
                                            } 
                                        }
                                    } 
 
                                    break;
                                    #endregion // CUD Operation 
                                default:
                                    Debug.Assert(this.batchRequestStream.State == BatchStreamState.EndBatch, "expecting end batch state");
                                    break;
                            } 
                        }
                        catch (Exception exception) 
                        { 
                            if (!WebUtil.IsCatchableExceptionType(exception))
                            { 
                                throw;
                            }

                            if (this.batchRequestStream.State == BatchStreamState.EndChangeSet) 
                            {
                                this.HandleChangesetException(exception, this.batchRequestHost, 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
                            { 
                                BatchServiceHost currentHost = (BatchServiceHost)this.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 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.batchRequestHost.Clear();
                            }
                        } 
                    }
 
                    BatchWriter.WriteEndBoundary(writer, this.batchBoundary); 
                    writer.Flush();
                } 
                finally
                {
                    this.batchRequestStream.Dispose();
                } 
            }
 
            #region Private methods. 

            ///  
            /// Gets the value of the given header from the given header collection
            /// 
            /// Dictionary with header names and values.
            /// name of the header whose value needs to be returned. 
            /// value of the given header.
            private static string GetValue(Dictionary headers, string headerName) 
            { 
                string headerValue;
                headers.TryGetValue(headerName, out headerValue); 
                return headerValue;
            }

            ///  
            /// Creates a batch host from the given headers
            ///  
            /// IDataServiceHost implementation host for this data service. 
            /// batch stream which contains the header information.
            /// content ids that are defined in the batch. 
            /// Part separator for host.
            /// Output writer.
            /// instance of the batch host which represents the current operation.
            private static BatchServiceHost CreateHostFromHeaders(IDataServiceHost host, BatchStream batchStream, HashSet contentIds, string boundary, StreamWriter writer) 
            {
                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 = GetValue(batchStream.ContentHeaders, XmlConstants.HttpContentID);
                if (!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(contentId)); 
                    }
 
                    if (!contentIds.Add(contentId))
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.DataService_ContentIdMustBeUniqueInBatch(contentId));
                    } 
                }
 
                CachedRequestParams requestParams = CreateRequestParams(host, batchStream); 
                return new BatchServiceHost(requestParams, batchStream.GetContentStream(), contentIdValue, boundary, writer);
            } 

            /// 
            /// Creates a new instance of CachedRequestParams given the header information
            ///  
            /// IDataServiceHost implementation host for this data service.
            /// batch stream which contains the header information. 
            /// instance of the CachedRequestParams with all request header information. 
            private static CachedRequestParams CreateRequestParams(IDataServiceHost host, BatchStream batchStream)
            { 
                string accept = GetValue(batchStream.ContentHeaders, XmlConstants.HttpRequestAccept);
                string acceptCharset = GetValue(batchStream.ContentHeaders, XmlConstants.HttpRequestAcceptCharset);
                string contentType = GetValue(batchStream.ContentHeaders, XmlConstants.HttpContentType);
                string headerIfMatch = GetValue(batchStream.ContentHeaders, XmlConstants.HttpRequestIfMatch); 
                string headerIfNoneMatch = GetValue(batchStream.ContentHeaders, XmlConstants.HttpRequestIfNoneMatch);
                string version = GetValue(batchStream.ContentHeaders, XmlConstants.HttpDataServiceVersion); 
                string maxVersion = GetValue(batchStream.ContentHeaders, XmlConstants.HttpMaxDataServiceVersion); 
                Uri absoluteServiceUri = RequestUriProcessor.GetServiceUri(host);
                Uri contentUri = RequestUriProcessor.GetAbsoluteUriFromReference( 
                    batchStream.ContentUri,                             // reference
                    absoluteServiceUri);                                // absoluteServiceUri

                return new CachedRequestParams( 
                    accept,
                    acceptCharset, 
                    contentType, 
                    GetHttpMethodName(batchStream.State),
                    headerIfMatch, 
                    headerIfNoneMatch,
                    version,
                    maxVersion,
                    contentUri, 
                    absoluteServiceUri);
            } 
 
            /// 
            /// Returns the http method name given the batch stream state 
            /// 
            /// state of the batch stream.
            /// returns the http method name
            private static string GetHttpMethodName(BatchStreamState state) 
            {
                Debug.Assert( 
                    state == BatchStreamState.Get || 
                    state == BatchStreamState.Post ||
                    state == BatchStreamState.Put || 
                    state == BatchStreamState.Delete ||
                    state == BatchStreamState.Merge,
                    "Expecting BatchStreamState (" + state + ") to be Delete, Get, Post or Put");
 
                switch (state)
                { 
                    case BatchStreamState.Delete: 
                        return XmlConstants.HttpMethodDelete;
                    case BatchStreamState.Get: 
                        return XmlConstants.HttpMethodGet;
                    case BatchStreamState.Post:
                        return XmlConstants.HttpMethodPost;
                    case BatchStreamState.Merge: 
                        return XmlConstants.HttpMethodMerge;
                    default: 
                        Debug.Assert(BatchStreamState.Put == state, "BatchStreamState.Put == state"); 
                        return XmlConstants.HttpMethodPut;
                } 
            }

            /// 
            /// Gets the resource from the segment enumerable. 
            /// 
            /// segment from which resource needs to be returned. 
            /// returns the resource contained in the request enumerable. 
            private static object GetResourceFromSegmentEnumerable(SegmentInfo segmentInfo)
            { 
                Debug.Assert(segmentInfo.RequestEnumerable != null, "The segment should always have the result");
                object[] results = (object[])segmentInfo.RequestEnumerable;
                Debug.Assert(results != null && results.Length == 1, "results != null && results.Length == 1");
                Debug.Assert(results[0] != null, "results[0] != null"); 
                return results[0];
            } 
 
            /// 
            /// Write the exception encountered in the middle of the changeset to the response 
            /// 
            /// exception encountered
            /// list of hosts
            /// changeset boundary for the current processing changeset 
            /// writer to which the response needs to be written
            private void HandleChangesetException( 
                Exception exception, 
                List changesetHosts,
                string changesetBoundary, 
                StreamWriter writer)
            {
                Debug.Assert(exception != null, "exception != null");
                Debug.Assert(changesetHosts != null, "changesetHosts != 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 right the last operation information. 
                // If there are no host, then just pass null.
                BatchServiceHost currentHost = null;
                if (changesetHosts.Count == 0)
                { 
                    currentHost = new BatchServiceHost(changesetBoundary, writer);
                } 
                else 
                {
                    currentHost = changesetHosts[changesetHosts.Count - 1]; 
                }

                ErrorHandler.HandleBatchProcessException(this, currentHost, exception, writer);
 
                // Write end boundary for the changeset
                BatchWriter.WriteEndBoundary(writer, changesetBoundary); 
                this.dataService.Provider.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));
                } 
            }

            /// 
            /// 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, this.host, batchHost.BoundaryString); 
                } 
                else
                { 
                    Action responseWriter = DataService.SerializeResponseBody(description, this);
                    if (responseWriter != null)
                    {
                        BatchWriter.WriteBoundaryAndHeaders(batchHost.Writer, this.host, batchHost.BoundaryString); 
                        batchHost.Writer.Flush();
                        responseWriter(batchHost.Writer.BaseStream); 
                        batchHost.Writer.WriteLine(); 
                    }
                    else 
                    {
                        BatchWriter.WriteBoundaryAndHeaders(batchHost.Writer, this.host, batchHost.BoundaryString);
                    }
                } 
            }
 
            #endregion Private methods. 
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      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.Diagnostics; 
    using System.IO;
    using System.Linq; 
    using System.ServiceModel; 
    using System.ServiceModel.Activation;
    using System.ServiceModel.Channels; 
    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
    /// System.Data.Object.ObjectContext or another class that provides IQueryable
    /// 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; 

        /// Host implementation for this data service. 
        private IDataServiceHost host;

        /// Data provider for this data service.
        private IDataServiceProvider provider; 

        /// Cached request headers. 
        private CachedRequestParams requestParams; 

        ///  dummy data service for batch requests. 
        private BatchDataService batchDataService;

        #endregion Private fields.
 
        #region Properties.
 
        /// Service configuration information. 
        DataServiceConfiguration IDataService.Configuration
        { 
            [DebuggerStepThrough]
            get { return this.configuration; }
        }
 
        /// Host implementation for this data service
        IDataServiceHost IDataService.Host 
        { 
            [DebuggerStepThrough]
            get { return this.host; } 
        }

        /// Data provider for this data service
        IDataServiceProvider IDataService.Provider 
        {
            [DebuggerStepThrough] 
            get 
            {
                Debug.Assert(this.provider != null, "this.provider != null -- otherwise EnsureProviderAndConfigForRequest didn't"); 
                return this.provider;
            }
        }
 
        /// Returns the instance of data service.
        IDataService IDataService.Instance 
        { 
            [DebuggerStepThrough]
            get { return this; } 
        }

        /// Cached request headers.
        CachedRequestParams IDataService.RequestParams 
        {
            [DebuggerStepThrough] 
            get { return this.requestParams; } 
        }
 
        /// 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 (!WebUtil.IsCatchableExceptionType(handlingException))
                { 
                    throw; 
                }
 
                args.Exception = handlingException;
            }
        }
 
        /// 
        /// 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 (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)
        { 
            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.host = 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
            {
                this.EnsureProviderAndConfigForRequest();
                Action writer = this.HandleRequest(); 
                Debug.Assert(writer != null, "writer != null");
                Message result = CreateMessage(MessageVersion.None, "", ((IDataServiceHost)httpHost).ResponseContentType, writer, this); 
                shouldDispose = false; 
                return result;
            } 
            finally
            {
                if (shouldDispose)
                { 
                    ((IDataService)this).DisposeDataSource();
                } 
            } 
        }
 
        /// Provides a host-agnostic entry point for request processing.
        public void ProcessRequest()
        {
            if (this.host == null) 
            {
                throw new InvalidOperationException(Strings.DataService_HostNotAttached); 
            } 

            try 
            {
                this.EnsureProviderAndConfigForRequest();
                Action writer = this.HandleRequest();
                if (writer != null) 
                {
                    writer(this.host.ResponseStream); 
                } 
            }
            finally 
            {
                ((IDataService)this).DisposeDataSource();
            }
        } 

        #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"); 
        } 

        ///  
        /// 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. 
        }

        #endregion Protected methods.
 
        #region Private methods.
 
        /// Checks that the specified  has a known version. 
        /// Service to check.
        private static void CheckVersion(IDataService service) 
        {
            Debug.Assert(service != null, "service != null");
            Debug.Assert(service.RequestParams != null, "service.RequestParams != null");
 
            // Check that the request/payload version is understood.
            string versionText = service.RequestParams.Version; 
            if (!String.IsNullOrEmpty(versionText)) 
            {
                KeyValuePair version; 
                if (!HttpProcessUtility.TryReadVersion(versionText, out version))
                {
                    throw DataServiceException.CreateBadRequestError(
                        Strings.DataService_VersionCannotBeParsed(versionText)); 
                }
 
                // Currently we only recognize an exact match. In future versions 
                // we may choose to allow different major/minor combinations.
                if (version.Key.Major != XmlConstants.DataServiceVersionCurrentMajor || 
                    version.Key.Minor != XmlConstants.DataServiceVersionCurrentMinor)
                {
                    string message = Strings.DataService_VersionNotSupported(
                        version.Key.ToString(2), 
                        XmlConstants.DataServiceVersionCurrentMajor,
                        XmlConstants.DataServiceVersionCurrentMinor); 
                    throw DataServiceException.CreateBadRequestError(message); 
                }
            } 

            // Check that the maximum version for the client will understand our response.
            versionText = service.RequestParams.MaxVersion;
            if (!String.IsNullOrEmpty(versionText)) 
            {
                KeyValuePair version; 
                if (!HttpProcessUtility.TryReadVersion(versionText, out version)) 
                {
                    throw DataServiceException.CreateBadRequestError( 
                        Strings.DataService_VersionCannotBeParsed(versionText));
                }

                if (version.Key.Major < XmlConstants.DataServiceVersionCurrentMajor || 
                    (version.Key.Major == XmlConstants.DataServiceVersionCurrentMajor &&
                     version.Key.Minor < XmlConstants.DataServiceVersionCurrentMinor)) 
                { 
                    string message = Strings.DataService_VersionTooLow(
                        version.Key.ToString(2), 
                        XmlConstants.DataServiceVersionCurrentMajor,
                        XmlConstants.DataServiceVersionCurrentMinor);
                    throw DataServiceException.CreateBadRequestError(message);
                } 
            }
        } 
 
        /// 
        /// Checks that if etag values are specified in the header, they must be valid. 
        /// 
        /// header values.
        private static void CheckETagValues(CachedRequestParams requestParams)
        { 
            Debug.Assert(requestParams != null, "requestParams != null");
            if (!IsETagValueValid(requestParams.IfMatch)) 
            { 
                throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError);
            } 

            if (!IsETagValueValid(requestParams.IfNoneMatch))
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError); 
            }
        } 
 
        /// 
        /// Returns false if the given etag value is not valid. 
        /// Look in http://www.ietf.org/rfc/rfc2616.txt?number=2616 (Section 14.26) for more information
        /// 
        /// etag value to be checked.
        /// returns true if the etag value is valid, otherwise returns false. 
        private static bool IsETagValueValid(string etag)
        { 
            if (String.IsNullOrEmpty(etag) || etag == XmlConstants.HttpAnyETag) 
            {
                return true; 
            }

            if (etag.Length <= 4 || etag[0] != 'W' || etag[1] != '/' || etag[2] != '"' || etag[etag.Length - 1] != '"')
            { 
                return false;
            } 
 
            for (int i = 3; i < etag.Length - 1; i++)
            { 
                // Format of etag looks something like: W/"etag property values"
                // according to HTTP RFC 2616, if someone wants to specify more than 1 etag value,
                // then need to specify something like this: W/"etag values", W/"etag values", ...
                // To make sure only one etag is specified, we need to ensure that 
                // only the third and last characters are quotes.
                // If " is part of the key value, it needs to be escaped. 
                if (etag[i] == '"') 
                {
                    return false; 
                }
            }

            return true; 
        }
 
        ///  
        /// 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 configuration for the specified service.
        /// Type of DataService with authorization methods. 
        /// Provider with metadata.
        /// Instance of the data source for the provider.
        /// 
        /// A (possibly shared) configuration implementation for the specified service. 
        /// 
        private static DataServiceConfiguration CreateConfiguration(Type dataServiceType, IDataServiceProvider provider, object dataSourceInstance) 
        { 
            Debug.Assert(dataServiceType != null, "dataServiceType != null");
 
            DataServiceConfiguration result = new DataServiceConfiguration(provider);
            result.InvokeStaticInitialization(dataServiceType);
            result.RegisterCallbacks(dataServiceType);
            result.ApplyToProvider(dataSourceInstance); 
            return result;
        } 
 
        /// 
        /// Creates a provider implementation that wraps the T type. 
        /// 
        /// Type of DataService with service operations.
        /// Instance of the data source for the provider.
        /// Service configuration information. 
        /// 
        /// A (possibly shared) provider implementation that wraps the T type. 
        ///  
        private static IDataServiceProvider CreateProvider(Type dataServiceType, object dataSourceInstance, out DataServiceConfiguration configuration)
        { 
            Debug.Assert(dataServiceType != null, "dataServiceType != null");
            Debug.Assert(dataSourceInstance != null, "dataSourceInstance != null");

            Type dataContextType = typeof(T); 
            Debug.Assert(
                dataContextType.IsAssignableFrom(dataSourceInstance.GetType()), 
                "dataContextType.IsAssignableFrom(dataSourceInstance.GetType()) -- otherwise the wrong data source instance was created."); 

            MetadataCacheItem metadata = MetadataCache.TryLookup(dataServiceType, dataSourceInstance); 
            bool metadataRequiresInitialization = metadata == null;
            if (metadataRequiresInitialization)
            {
                metadata = new MetadataCacheItem(dataContextType); 
            }
 
            BaseServiceProvider result; 
            if (typeof(ObjectContext).IsAssignableFrom(dataContextType))
            { 
                result = new ObjectContextServiceProvider(metadata, dataSourceInstance);
            }
            else
            { 
                result = new ReflectionServiceProvider(metadata, dataSourceInstance);
            } 
 
            if (metadataRequiresInitialization)
            { 
                // Populate metadata in provider.
                result.PopulateMetadata();
                result.AddOperationsFromType(dataServiceType);
 
                // Create and cache configuration, which goes hand-in-hand with metadata.
                metadata.Configuration = CreateConfiguration(dataServiceType, result, dataSourceInstance); 
                metadata.Seal(); 

                MetadataCache.AddCacheItem(dataServiceType, dataSourceInstance, metadata); 
            }

            configuration = metadata.Configuration;
 
            return (IDataServiceProvider)result;
        } 
 
        /// 
        /// 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(IDataServiceHost 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. 
        /// Request parameters.
        private static void ValidateRequest(CachedRequestParams requestParams) 
        {
            if (!String.IsNullOrEmpty(requestParams.IfMatch) && !String.IsNullOrEmpty(requestParams.IfNoneMatch))
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_BothIfMatchAndIfNoneMatchHeaderSpecified); 
            }
        } 
 
        /// 
        /// 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.Host != null, "dataService.Host != null");
 
            CachedRequestParams requestParams = dataService.RequestParams;
            CheckVersion(dataService); 
            CheckETagValues(dataService.RequestParams); 

            ResourceContainer lastSegmentContainer = description.LastSegmentInfo.TargetContainer; 
            if (requestParams.AstoriaHttpVerb == AstoriaVerbs.GET)
            {
                if (lastSegmentContainer != null)
                { 
                    dataService.Configuration.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 (requestParams.AstoriaHttpVerb == AstoriaVerbs.POST)
                {
                    newDescription = HandlePostOperation(description, dataService); 
                    if (description.LinkUri)
                    { 
                        statusCode = 204;   // 204 - No Content 
                        shouldWriteBody = false;
                    } 
                    else
                    {
                        statusCode = 201;   // 201 - Created.
                    } 
                }
                else if (requestParams.AstoriaHttpVerb == AstoriaVerbs.PUT || 
                         requestParams.AstoriaHttpVerb == AstoriaVerbs.MERGE) 
                {
                    if (lastSegmentContainer != null) 
                    {
                        if (requestParams.AstoriaHttpVerb == AstoriaVerbs.PUT)
                        {
                            dataService.Configuration.CheckResourceRights(lastSegmentContainer, EntitySetRights.WriteReplace); 
                        }
                        else 
                        { 
                            dataService.Configuration.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);
                    statusCode = 204;   // 204 - No Content 
                } 
                else if (requestParams.AstoriaHttpVerb == AstoriaVerbs.DELETE)
                { 
                    if (lastSegmentContainer != null)
                    {
                        dataService.Configuration.CheckResourceRights(lastSegmentContainer, EntitySetRights.WriteDelete);
                    } 

                    HandleDeleteOperation(description, dataService); 
                    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.
            dataService.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.
            dataService.Host.ResponseVersion = XmlConstants.DataServiceVersionCurrent; 

            dataService.Host.ResponseStatusCode = statusCode; 
 
            if (shouldWriteBody)
            { 
                dataService.Host.ResponseVersion = XmlConstants.DataServiceVersionCurrent;

                // 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.Host != null, "dataService.Host != null");
 
            CachedRequestParams requestParams = dataService.RequestParams;
 
            // 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 (!description.IsSingleResult || (description.ExpandPaths != null && description.ExpandPaths.Count != 0)) 
            { 
                if (!String.IsNullOrEmpty(requestParams.IfMatch) || !String.IsNullOrEmpty(requestParams.IfNoneMatch))
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagSpecifiedForCollection(requestParams.AbsoluteRequestUri));
                }
            }
 
            if (requestParams.AstoriaHttpVerb == AstoriaVerbs.PUT ||
                requestParams.AstoriaHttpVerb == AstoriaVerbs.MERGE) 
            { 
                ResourceContainer container;
                object actualEntity = GetContainerAndActualEntityInstance(dataService.Provider, description, out container); 
                dataService.Host.ResponseETag = WebUtil.GetETagValue(dataService.Provider, actualEntity, container);
                return EmptyStreamWriter;
            }
 
            // 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.TargetElementType, 
                requestParams.Accept,
                description.MimeType,
                dataService.Host);
 
            // check for etags first
            // If no etag is specified, then do the normal stuff - run the query and serialize the result 
            if (description.TargetSource == RequestTargetSource.ServiceOperation || 
                description.TargetSource == RequestTargetSource.None ||
                !description.IsSingleResult) 
            {
                Debug.Assert(
                    String.IsNullOrEmpty(requestParams.IfMatch) && String.IsNullOrEmpty(requestParams.IfNoneMatch),
                    "No etag can be specified for collection"); 
                Encoding encoding = GetRequestAcceptEncoding(responseFormat, requestParams.AcceptCharset);
                IEnumerator queryResults = WebUtil.GetRequestEnumerator(description.RequestEnumerable); 
                try 
                {
                    bool hasMoved = queryResults.MoveNext(); 

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

                    dataService.Host.ResponseContentType = HttpProcessUtility.BuildContentType(dataService.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.
        /// CLR element type.
        /// Accept header value.
        /// Required MIME type. 
        /// Host implementation for this data service.
        ///  
        /// The content format for the resource; Unknown if it cannot be determined statically. 
        /// 
        private static ContentFormat SelectResponseFormatForType( 
            RequestTargetKind targetKind,
            Type elementType,
            string acceptTypesText,
            string mimeType, 
            IDataServiceHost host)
        { 
            ContentFormat responseFormat; 
            if (targetKind == RequestTargetKind.PrimitiveValue)
            { 
                responseFormat = SelectPrimitiveContentType(elementType, acceptTypesText, mimeType, host);
            }
#if ASTORIA_OPEN_OBJECT
            else if (targetKind != RequestTargetKind.OpenPropertyValue && targetKind != RequestTargetKind.OpenProperty) 
#else
            else 
#endif 
            {
                bool entityTarget = targetKind == RequestTargetKind.Resource; 
                responseFormat = SelectResponseFormat(host, acceptTypesText, entityTarget);
                if (responseFormat == ContentFormat.Unsupported)
                {
                    throw new DataServiceException(415, Strings.DataServiceException_UnsupportedMediaType); 
                }
            } 
#if ASTORIA_OPEN_OBJECT 
            else
            { 
                // We cannot negotiate a format until we know what the value is for the object.
                responseFormat = ContentFormat.Unknown;
            }
#endif 

            return responseFormat; 
        } 

        /// Selects the correct content format for a primitive type. 
        /// CLR element 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(Type targetElementType, string acceptTypesText, string requiredContentType, IDataServiceHost host) 
        { 
            Debug.Assert(targetElementType != null, "targetElementType != null");
 
            string contentType;
            ContentFormat responseFormat = WebUtil.GetResponseFormatForPrimitiveValue(targetElementType, out contentType);
            requiredContentType = requiredContentType ?? contentType;
            host.ResponseContentType = HttpProcessUtility.SelectRequiredMimeType( 
                acceptTypesText,        // acceptTypesText
                new string[] { requiredContentType },    // exactContentType 
                requiredContentType);   // inexactContentType 
            return responseFormat;
        } 

        /// 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");

            CachedRequestParams requestParams = dataService.RequestParams;
            if (!String.IsNullOrEmpty(requestParams.IfMatch) || !String.IsNullOrEmpty(requestParams.IfNoneMatch)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagSpecifiedForPost); 
            } 

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

            Stream requestStream = dataService.Host.RequestStream; 
            if (requestStream == null) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_NullRequestStream); 
            }

            string mimeType;
            System.Text.Encoding encoding; 
            HttpProcessUtility.ReadContentType(dataService.Host.RequestContentType, out mimeType, out encoding);
            ContentFormat requestFormat = WebUtil.SelectRequestFormat(mimeType, description); 
 
            object entity = null;
            Deserializer deserializer = null; 
            try
            {
                switch (requestFormat)
                { 
                    case ContentFormat.Json:
                        StreamReader streamReader = new StreamReader(requestStream, encoding); 
                        deserializer = new JsonDeserializer( 
                            streamReader,
                            false /*update*/, 
                            dataService,
                            UpdateTracker.CreateUpdateTracker(description, dataService.Provider));
                        break;
                    case ContentFormat.Atom: 
                        SyndicationFormatterFactory factory = new Atom10FormatterFactory();
                        deserializer = new SyndicationDeserializer( 
                            requestStream,  // stream 
                            encoding,       // encoding
                            dataService,    // dataService 
                            false,          // update
                            factory,
                            UpdateTracker.CreateUpdateTracker(description, dataService.Provider));       // factory
                        break; 
                    case ContentFormat.PlainXml:
                        deserializer = new PlainXmlDeserializer( 
                            requestStream, 
                            encoding,
                            dataService, 
                            false /*update*/,
                            UpdateTracker.CreateUpdateTracker(description, dataService.Provider));
                        break;
                    default: 
                        throw new DataServiceException(415, Strings.BadRequest_UnsupportedMediaForPost(mimeType));
                } 
 
                Debug.Assert(deserializer != null, "deserializer != null");
                entity = deserializer.HandlePostRequest(description); 
                Debug.Assert(entity != null, "entity != null");

                if (deserializer.Tracker != null)
                { 
                    deserializer.Tracker.FireNotifications(dataService.Instance);
                } 
 
                return RequestDescription.CreateSingleResultRequestDescription(
                        description, entity, description.LastSegmentInfo.TargetContainer); 
            }
            finally
            {
                WebUtil.Dispose(deserializer); 
            }
        } 
 
        /// 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");
 
            if (!description.IsSingleResult) 
            {
                throw DataServiceException.CreateMethodNotAllowed( 
                    Strings.BadRequest_InvalidUriForPutOperation(dataService.RequestParams.AbsoluteRequestUri),
                    dataService.Configuration.GetAllowedMethods(description));
            }
 
            if (!String.IsNullOrEmpty(dataService.RequestParams.IfNoneMatch))
            { 
                throw DataServiceException.CreateBadRequestError(Strings.DataService_IfNoneMatchHeaderNotSupportedInPut); 
            }
            else if (description.LinkUri && !String.IsNullOrEmpty(dataService.RequestParams.IfMatch)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagsNotAllowedForLinkOperations);
            }
            else if (description.Property != null && description.Property.IsOfKind(ResourcePropertyKind.Key)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotUpdateKeyProperties(description.Property.Name)); 
            } 

            Stream requestStream = dataService.Host.RequestStream; 
            if (requestStream == null)
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_NullRequestStream);
            } 

            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.RequestParams != null, "dataService.RequestParams != null"); 

            // 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.LinkUri)
            { 
                HandleUnbindOperation(description, dataService); 
            }
            else if ( 
#if ASTORIA_OPEN_OBJECT
                (description.TargetKind == RequestTargetKind.OpenProperty) ||
#endif
                (description.IsSingleResult && description.TargetKind == RequestTargetKind.Resource)) 
            {
#if ASTORIA_OPEN_OBJECT 
                Debug.Assert( 
                    description.LastSegmentInfo.TargetContainer != null || description.TargetKind == RequestTargetKind.OpenProperty,
                    "description.LastSegmentInfo.TargetContainer != null || TargetKind == OpenProperty"); 

#endif

                if (description.RequestEnumerable == null) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation); 
                } 

                // 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*/);
                ResourceContainer container = description.LastSegmentInfo.TargetContainer;
 
                //
 
                object actualEntity = dataService.Provider.ResolveResource(entity); 

                // For open properties, we need to make sure that they refer to resource types 
#if ASTORIA_OPEN_OBJECT
                if (description.TargetKind == RequestTargetKind.OpenProperty)
                {
                    // Verify that the resource is an entity type. Otherwise we need to throw 
                    ResourceType resourceType = dataService.Provider.GetResourceType(actualEntity.GetType());
                    if (resourceType == null || resourceType.ResourceTypeKind != ResourceTypeKind.EntityType) 
                    { 
                        throw DataServiceException.CreateBadRequestError(
                            Strings.DataService_TypeNotValidForDeleteOperation(dataService.RequestParams.AbsoluteRequestUri)); 
                    }

                    container = dataService.Provider.GetContainerForResourceType(resourceType.Type);
                } 
#endif
 
                if (description.Property != null) 
                {
                    Debug.Assert(container != null, "container != null"); 
                    dataService.Configuration.CheckResourceRights(container, EntitySetRights.WriteDelete);
                }

                CheckForETagInDeleteOperation(actualEntity, entity, container, dataService.RequestParams, dataService.Provider); 
                dataService.Provider.DeleteResource(entity);
 
                // 
#if ASTORIA_OPEN_OBJECT
                if (description.TargetKind != RequestTargetKind.OpenProperty) 
#endif
                {
                    UpdateTracker.FireNotification(dataService.Instance, 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 (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'). 
                ResourceContainer container;    // Resource Container to which the parent entity belongs to.
                object resource = Deserializer.GetResourceToModify(description, dataService, false /*allowCrossReference*/, out securityResource, out container); 
 
                //
 
                object actualEntity = dataService.Provider.ResolveResource(securityResource);
                CheckForETagInDeleteOperation(actualEntity, securityResource, container, dataService.RequestParams, dataService.Provider);

                // 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.Provider);
                UpdateTracker.FireNotification(dataService.Instance, actualEntity, container, UpdateOperations.Change); 
            } 
#if ASTORIA_OPEN_OBJECT
            else if (description.TargetKind == RequestTargetKind.OpenPropertyValue) 
            {
                object securityResource;
                object resource = Deserializer.GetResourceToModify(description, dataService, out securityResource);
 
                //
 
                object actualEntity = dataService.Provider.ResolveResource(resource); 
                ResourceContainer container = dataService.Provider.GetContainerForResourceType(actualEntity.GetType());
                CheckForETagInDeleteOperation(actualEntity, resource, container, dataService.RequestParams, dataService.Provider); 

                // 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.Provider);
            } 
#endif
            else 
            { 
                throw DataServiceException.CreateMethodNotAllowed(
                    Strings.BadRequest_InvalidUriForDeleteOperation(dataService.RequestParams.AbsoluteRequestUri), 
                    dataService.Configuration.GetAllowedMethods(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; 
            if (description.TargetKind == RequestTargetKind.Metadata) 
            {
                exactContentType = new string[] { XmlConstants.MimeMetadata }; 
                format = ContentFormat.MetadataDocument;
                mime = HttpProcessUtility.SelectRequiredMimeType(
                    dataService.RequestParams.Accept,   // acceptTypesText
                    exactContentType,                   // exactContentType 
                    XmlConstants.MimeApplicationXml);   // inexactContentType
            } 
            else if (description.TargetKind == RequestTargetKind.ServiceDirectory) 
            {
                exactContentType = new string[] { XmlConstants.MimeApplicationAtomService, XmlConstants.MimeApplicationJson, XmlConstants.MimeApplicationXml }; 
                mime = HttpProcessUtility.SelectRequiredMimeType(
                    dataService.RequestParams.Accept,   // 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(dataService.RequestParams.AcceptCharset); 
                dataService.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"); 
            CachedRequestParams requestParams = dataService.RequestParams;
            Debug.Assert( 
                String.IsNullOrEmpty(requestParams.IfMatch) || String.IsNullOrEmpty(requestParams.IfNoneMatch), 
                "Both If-Match and If-None-Match header cannot be specified");
            IEnumerator queryResults = null; 
            try
            {
                if (requestParams.AstoriaHttpVerb == AstoriaVerbs.GET)
                { 
                    bool writeResponse = true;
 
                    // Get the index of the last resource in the request uri 
                    int parentResourceIndex = description.GetIndexOfTargetEntityResource();
                    SegmentInfo parentEntitySegment = description.SegmentInfos[parentResourceIndex]; 
                    queryResults = RequestDescription.GetSingleResultFromEnumerable(parentEntitySegment);
                    object resource = queryResults.Current;
                    string etagValue = null;
 
                    if (description.LinkUri)
                    { 
                        // No need to worry about etags while performing link operations 
                        // No etags can be specified also while performing link operations
                        if (requestParams.IfMatch != null || requestParams.IfNoneMatch != null) 
                        {
                            throw DataServiceException.CreateBadRequestError(Strings.DataService_ETagsNotAllowedForLinkOperations);
                        }
 
                        if (resource == null)
                        { 
                            throw DataServiceException.CreateResourceNotFound(description.LastSegmentInfo.Identifier); 
                        }
                    } 
                    else
                    {
                        ResourceContainer container = null;
                        if (resource != null) 
                        {
                            container = WebUtil.GetResourceContainer(resource, parentEntitySegment, dataService.Provider); 
                        } 

                        etagValue = WebUtil.CompareAndGetETag( 
                            resource, resource, container, dataService.Provider, requestParams, out writeResponse);

                        if (resource == null && description.TargetKind == RequestTargetKind.Resource)
                        { 
                            Debug.Assert(description.Property != null, "non-open type property");
 
                            // If you are querying reference nav property and the value is null, 
                            // return 204 - No Content e.g. /Customers(1)/BestFriend
                            dataService.Host.ResponseStatusCode = 204; // No Content 
                            return EmptyStreamWriter;
                        }

                        WriteETagValueInResponseHeader(description, etagValue, dataService.Host); 
                    }
 
                    if (writeResponse) 
                    {
                        int lastResourceIndex = description.GetIndexOfTargetEntityResource(); 
                        return WriteSingleElementResponse(description, responseFormat, queryResults, lastResourceIndex, etagValue, dataService);
                    }
                    else
                    { 
                        dataService.Host.ResponseStatusCode = 304; // Not Modified
                        return EmptyStreamWriter; 
                    } 
                }
                else 
                {
                    Debug.Assert(requestParams.AstoriaHttpVerb == AstoriaVerbs.POST, "Must be POST method");
                    ResourceContainer container;
                    object actualEntity = GetContainerAndActualEntityInstance(dataService.Provider, description, out container); 
                    dataService.Host.ResponseLocation = Serializer.GetUri(actualEntity, dataService.Provider, container, requestParams.AbsoluteServiceUri).AbsoluteUri;
                    string etagValue = WebUtil.GetETagValue(dataService.Provider, actualEntity, container); 
                    queryResults = RequestDescription.GetSingleResultFromEnumerable(description.LastSegmentInfo); 
                    return WriteSingleElementResponse(description, responseFormat, queryResults, description.SegmentInfos.Length - 1, etagValue, dataService);
                } 
            }
            catch
            {
                WebUtil.Dispose(queryResults); 
                throw;
            } 
        } 

#if ASTORIA_OPEN_OBJECT 

        /// 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 + " is open property or open property value");
            WebUtil.CheckResourceExists(element != null, description.LastSegmentInfo.Identifier); 
            Type elementType = element.GetType();
            ResourceType resourceType = dataService.Provider.GetResourceType(elementType); 
 
            // This resource wouldn't be visible during serialization, so we treat is as 404.
            if (resourceType == null) 
            {
                throw new InvalidOperationException(Strings.DataService_InvalidResourceType(elementType.FullName));
            }
 
            // Determine the appropriate target type based on the kind of resource.
            bool rawValue = description.TargetKind == RequestTargetKind.OpenPropertyValue; 
            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) 
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ValuesCanBeReturnedForPrimitiveTypesOnly); 
                    }
                    else
                    {
                        targetKind = RequestTargetKind.Resource; 
                    }
 
                    break; 
            }
 
            if (description.LinkUri)
            {
                targetKind = RequestTargetKind.Link;
            } 

            return SelectResponseFormatForType(targetKind, elementType, dataService.RequestParams.Accept, null, dataService.Host); 
        } 

#endif 

        /// 
        /// 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
            {
                if (parentResourceIndex != description.SegmentInfos.Length - 1) 
                {
                    // Dispose the old enumerator 
                    WebUtil.Dispose(queryResults); 

                    // get the resource which need to be written 
                    queryResults = RequestDescription.GetSingleResultFromEnumerable(description.LastSegmentInfo);
                }

                // If we had to wait until we got a value to determine the valid contents, try that now. 
#if ASTORIA_OPEN_OBJECT
                if (responseFormat == ContentFormat.Unknown) 
                { 
                    responseFormat = ResolveUnknownFormat(description, queryResults.Current, dataService);
                } 
#else
                Debug.Assert(responseFormat != ContentFormat.Unknown, "responseFormat != ContentFormat.Unknown");
#endif
 
                // Write the etag header
                WriteETagValueInResponseHeader(description, etagValue, dataService.Host); 
 
                Encoding encoding = GetRequestAcceptEncoding(responseFormat, dataService.RequestParams.AcceptCharset);
                dataService.Host.ResponseContentType = HttpProcessUtility.BuildContentType(dataService.Host.ResponseContentType, encoding); 
                return new ResponseBodyWriter(
                    encoding,
                    true /* hasMoved */,
                    dataService, 
                    queryResults,
                    description, 
                    responseFormat).Write; 
            }
            catch 
            {
                WebUtil.Dispose(queryResults);
                throw;
            } 
        }
 
        ///  
        /// Write the etag header value in the response
        ///  
        /// description about the request made
        /// etag value that needs to be written.
        /// Host implementation for this data service.
        private static void WriteETagValueInResponseHeader(RequestDescription requestDescription, string etagValue, IDataServiceHost host) 
        {
            Debug.Assert(requestDescription.IsSingleResult, "requestDescription.IsSingleResult"); 
            if ((requestDescription.ExpandPaths == null || requestDescription.ExpandPaths.Count == 0) 
                && !String.IsNullOrEmpty(etagValue))
            { 
                host.ResponseETag = etagValue;
            }
        }
 
        /// 
        /// Returns the actual entity instance and its containers for the resource in the description results. 
        ///  
        /// Data provider 
        /// 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(
            IDataServiceProvider provider, RequestDescription description, out ResourceContainer 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] = provider.ResolveResource(results[0]);
            container = description.LastSegmentInfo.TargetContainer; 
#if ASTORIA_OPEN_OBJECT
            if (container == null) 
            { 
                // Open types will not have TargetContainer set, but they don't support MEST either.
                Debug.Assert( 
                    RequestTargetKind.OpenProperty == description.LastSegmentInfo.TargetKind,
                    "RequestTargetKind.OpenProperty == description.LastSegmentInfo.TargetKind(" + description.LastSegmentInfo.TargetKind + " - otherwise, why is TargetContainer null for POST target?");
                container = provider.GetContainerForResourceType(results[0].GetType());
                Debug.Assert( 
                    container != null,
                    "container != null -- otherwise results[0].GetType() (" + results[0].GetType() + ") didn't work."); 
            } 
#else
            Debug.Assert(container != null, "description.LastSegmentInfo.TargetContainer != null"); 
#endif

            return results[0];
        } 

        ///  
        /// Check for etag values for the given resource in DeleteOperation 
        /// 
        /// resource whose etag value needs to be compared to the one given in the request header 
        /// token as returned by the IUpdatable.GetResource method.
        /// resource container to which the resource belongs to.
        /// request headers
        /// Data provider  
        private static void CheckForETagInDeleteOperation(
            object actualEntityInstance, 
            object entityToken, 
            ResourceContainer container,
            CachedRequestParams requestParams, 
            IDataServiceProvider provider)
        {
            Debug.Assert(actualEntityInstance != null, "actualEntityInstance != null");
            Debug.Assert(entityToken != null, "entityToken != null"); 

            // If this method is called for Update, we need to pass the token object as well as the actual instance. 
            // The actual instance is used to determine the type that's necessary to find out the etag properties. 
            // The token is required to pass back to IUpdatable interface, if we need to get the values for etag properties.
            if (!String.IsNullOrEmpty(requestParams.IfNoneMatch)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_IfNoneMatchHeaderNotSupportedInDelete);
            }
 
            ICollection etagProperties = provider.GetETagProperties(container.Name, actualEntityInstance.GetType());
            if (etagProperties.Count == 0) 
            { 
                if (requestParams.IfMatch != null)
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.Serializer_NoETagPropertiesForType);
                }
            }
            else if (String.IsNullOrEmpty(requestParams.IfMatch)) 
            {
                string typeName = WebUtil.GetTypeName(provider, actualEntityInstance.GetType()); 
                throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotPerformDeleteOperationWithoutETag(typeName)); 
            }
            else if (requestParams.IfMatch != XmlConstants.HttpAnyETag) 
            {
                string etagValue = WebUtil.GetETagValue(entityToken, etagProperties, provider);
                if (etagValue != requestParams.IfMatch)
                { 
                    throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch);
                } 
            } 
        }
 
        /// No-op method for a stream-writing action.
        /// Stream to write to.
        private static void EmptyStreamWriter(Stream stream)
        { 
        }
 
        ///  
        /// 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"); 
 
            object parentEntity;
            Deserializer.GetResourceToModify(description, dataService, out parentEntity); 
            if (description.Property != null)
            {
                if (description.Property.Kind == ResourcePropertyKind.ResourceReference)
                { 
                    dataService.Provider.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.Provider.RemoveReferenceFromCollection(parentEntity, description.Property.Name, childEntity);
                } 
            }
            else 
            { 
                if (description.LastSegmentInfo.HasKeyValues)
                { 
                    object childEntity = Deserializer.GetResource(description.LastSegmentInfo, null, dataService, true /*checkForNull*/);
                    dataService.Provider.RemoveReferenceFromCollection(parentEntity, description.ContainerName, childEntity);
                }
                else 
                {
                    dataService.Provider.SetReference(parentEntity, description.ContainerName, null); 
                } 
            }
        } 

        /// 
        /// 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 (mime == XmlConstants.MimeApplicationJson) 
            {
                return ContentFormat.Json;
            }
            else if (mime == XmlConstants.MimeApplicationAtom) 
            {
                return ContentFormat.Atom; 
            } 
            else
            { 
                Debug.Assert(
                    mime == XmlConstants.MimeApplicationXml || mime == XmlConstants.MimeTextXml,
                    "expecting application/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.host != null, "this.host != null");
            Action writer; 
            try 
            {
                if (this.host is HttpContextServiceHost) 
                {
                    ((HttpContextServiceHost)this.host).VerifyQueryParameters();
                }
 
                RequestDescription description = this.ProcessIncomingRequestUriAndCacheHeaders();
                this.OnStartProcessingRequest(new ProcessRequestArgs(this.requestParams.AbsoluteRequestUri, false /*isBatchOperation*/)); 
                if (description.TargetKind != RequestTargetKind.Batch) 
                {
                    writer = this.HandleNonBatchRequest(description); 
                }
                else
                {
                    writer = this.HandleBatchRequest(); 
                }
            } 
            catch (Exception exception) 
            {
                // Exception should be re-thrown if not handled. 
                if (!WebUtil.IsCatchableExceptionType(exception))
                {
                    throw;
                } 

                string accept = (this.requestParams != null) ? this.requestParams.Accept : null; 
                string acceptCharset = (this.requestParams != null) ? this.requestParams.AcceptCharset : 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"); 
            description = ProcessIncomingRequest(description, this);
 
            if (this.requestParams.AstoriaHttpVerb != AstoriaVerbs.GET) 
            {
                this.provider.SaveChanges(); 
            }

            return (description == null) ? EmptyStreamWriter : SerializeResponseBody(description, this);
        } 

        /// Handle the batch request. 
        /// Returns the delegate which takes the response stream for writing the response. 
        private Action HandleBatchRequest()
        { 
            // Verify the HTTP method.
            if (this.requestParams.AstoriaHttpVerb != AstoriaVerbs.POST)
            {
                throw DataServiceException.CreateMethodNotAllowed( 
                    Strings.DataService_BatchResourceOnlySupportsPost,
                    XmlConstants.HttpMethodPost); 
            } 

            CheckVersion(this); 

            // Verify the content type and get the boundary string
            Encoding encoding;
            string boundary; 

            if (!BatchStream.GetBoundaryAndEncodingFromMultipartMixedContentType(this.requestParams.ContentType, out boundary, out encoding) || 
                String.IsNullOrEmpty(boundary)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_InvalidContentTypeForBatchRequest); 
            }

            // Write the response headers
            this.host.ResponseStatusCode = 202; // OK 
            this.host.ResponseCacheControl = XmlConstants.HttpCacheControlNoCache;
 
            string batchBoundary = XmlConstants.HttpMultipartBoundaryBatchResponse + '_' + Guid.NewGuid().ToString(); 
            this.host.ResponseContentType = String.Format(
                System.Globalization.CultureInfo.InvariantCulture, 
                "{0}; {1}={2}",
                XmlConstants.MimeMultiPartMixed,
                XmlConstants.HttpMultipartBoundary,
                batchBoundary); 

            BatchStream batchStream = new BatchStream(this.host.RequestStream, boundary, encoding, true); 
            this.batchDataService = new BatchDataService(this, batchStream, batchBoundary); 
            return this.batchDataService.HandleBatchContent;
        } 

        /// Creates the provider and configuration as necessary to be used for this request.
        private void EnsureProviderAndConfigForRequest()
        { 
            if (this.provider == null)
            { 
                Type dataServiceType = this.GetType(); 
                object dataSourceInstance = this.CreateDataSource();
                if (dataSourceInstance == null) 
                {
                    throw new InvalidOperationException(Strings.DataService_CreateDataSourceNull);
                }
 
                this.provider = CreateProvider(dataServiceType, dataSourceInstance, out this.configuration);
            } 
            else 
            {
                Debug.Assert(this.configuration != null, "this.configuration != null -- otherwise this.provider was ----signed with no configuration"); 
            }
        }

        ///  
        /// Processes the incoming request and cache all the request headers
        ///  
        /// description about the request uri. 
        private RequestDescription ProcessIncomingRequestUriAndCacheHeaders()
        { 
            this.requestParams = new CachedRequestParams(
                this.host.RequestAccept,
                this.host.RequestAcceptCharSet,
                this.host.RequestContentType, 
                this.host.RequestHttpMethod,
                this.host.RequestIfMatch, 
                this.host.RequestIfNoneMatch, 
                this.host.RequestVersion,
                this.host.RequestMaxVersion, 
                RequestUriProcessor.GetAbsoluteRequestUri(this.host),
                RequestUriProcessor.GetServiceUri(this.host));

            ValidateRequest(this.requestParams); 
            return RequestUriProcessor.ProcessRequestUri(this.requestParams.AbsoluteRequestUri, this);
        } 
 
        #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 batchRequestHost = new List(); 
 
            /// Number of CUD operations encountered in the current changeset.
            private int changeSetElementCount; 

            /// Batch Host which caches the request headers and response headers per operation within a changeset.
            private IDataServiceHost host;
 
            #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.
            DataServiceConfiguration IDataService.Configuration 
            {
                get { return this.dataService.Configuration; }
            }
 
            /// Host implementation for the batch data service.
            IDataServiceHost IDataService.Host 
            { 
                get { return this.host; }
            } 

            /// Data provider for this data service.
            IDataServiceProvider IDataService.Provider
            { 
                get { return this.dataService.Provider; }
            } 
 
            /// Instance of the data provider.
            IDataService IDataService.Instance 
            {
                get { return this.dataService.Instance; }
            }
 
            /// Gets the cached request headers.
            CachedRequestParams IDataService.RequestParams 
            { 
                get { return ((BatchServiceHost)this.host).RequestParams; }
            } 

            /// 
            /// 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)
            { 
                this.dataService.InternalApplyingExpansions(queryable, expandPaths);
            }

            /// Processes a catchable exception. 
            /// The arguments describing how to handle the exception.
            void IDataService.InternalHandleException(HandleExceptionArgs args) 
            { 
                this.dataService.InternalHandleException(args);
            } 

            /// 
            /// 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) 
            {
                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. 
            object IDataService.GetResource(RequestDescription description, int segmentIndex, string typeFullName)
            { 
                if (description.SegmentInfos[0].Identifier.StartsWith("$", StringComparison.Ordinal))
                {
                    Debug.Assert(segmentIndex >= 0 && segmentIndex < description.SegmentInfos.Length, "segment index must be a valid one");
                    if (description.SegmentInfos[segmentIndex].RequestEnumerable == null) 
                    {
                        object resource = GetResourceFromSegmentEnumerable(description.SegmentInfos[0]); 
                        for (int i = 1; i <= segmentIndex; i++) 
                        {
                            resource = ((IDataService)this).Provider.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 GetResourceFromSegmentEnumerable(description.SegmentInfos[segmentIndex]); 
                    }
                } 
 
                return Deserializer.GetResource(description.SegmentInfos[segmentIndex], typeFullName, ((IDataService)this), false /*checkForNull*/);
            } 

            /// 
            /// Dispose the data source instance
            ///  
            void IDataService.DisposeDataSource()
            { 
                this.dataService.DisposeDataSource(); 
            }
 
            /// 
            /// This method is called before a request is processed.
            /// 
            /// Information about the request that is going to be processed. 
            void IDataService.InternalOnStartProcessingRequest(ProcessRequestArgs args)
            { 
                this.dataService.InternalOnStartProcessingRequest(args); 
            }
 
            #endregion

            /// 
            /// Handle the batch content 
            /// 
            /// response stream for writing batch response 
            internal void HandleBatchContent(Stream responseStream) 
            {
                BatchServiceHost batchHost = null; 
                RequestDescription description;
                string changesetBoundary = null;
                Exception exceptionEncountered = null;
 
                try
                { 
                    StreamWriter writer = new StreamWriter(responseStream, HttpProcessUtility.FallbackEncoding); 
                    while (!this.batchLimitExceeded && this.batchRequestStream.State != BatchStreamState.EndBatch)
                    { 
                        // clear the host from the last operation
                        this.host = 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);
                                    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)
                                    { 
                                        Debug.Assert(!String.IsNullOrEmpty(changesetBoundary), "!String.IsNullOrEmpty(changesetBoundary)");
 
                                        // Save all the changes and write the response 
                                        this.dataService.Provider.SaveChanges();
 
                                        Debug.Assert(this.batchRequestHost.Count == this.batchRequestDescription.Count, "counts must be the same");
                                        for (int i = 0; i < this.batchRequestDescription.Count; i++)
                                        {
                                            this.host = this.batchRequestHost[i]; 
                                            this.WriteRequest(this.batchRequestDescription[i], this.batchRequestHost[i]);
                                        } 
 
                                        BatchWriter.WriteEndBoundary(writer, changesetBoundary);
                                    } 
                                    else
                                    {
                                        this.HandleChangesetException(exceptionEncountered, this.batchRequestHost, changesetBoundary, writer);
                                    } 

                                    break; 
                                    #endregion //EndChangeSet 
                                case BatchStreamState.Get:
                                    #region GET Operation 
                                    this.IncreaseBatchCount();
                                    batchHost = CreateHostFromHeaders(
                                        this.dataService.Host,
                                        this.batchRequestStream, 
                                        this.contentIds,
                                        this.batchBoundary, 
                                        writer); 
                                    this.host = batchHost;
 
                                    // it must be GET operation
                                    Debug.Assert(this.host.RequestHttpMethod == XmlConstants.HttpMethodGet, "this.host.RequestHttpMethod == XmlConstants.HttpMethodGet");
                                    Debug.Assert(this.batchRequestDescription.Count == 0, "this.batchRequestDescription.Count == 0");
                                    Debug.Assert(this.batchRequestHost.Count == 0, "this.batchRequestHost.Count == 0"); 

                                    this.dataService.InternalOnStartProcessingRequest(new ProcessRequestArgs(this.host.AbsoluteRequestUri, true /*isBatchOperation*/)); 
                                    description = RequestUriProcessor.ProcessRequestUri(this.host.AbsoluteRequestUri, this); 
                                    description = ProcessIncomingRequest(description, this);
                                    this.WriteRequest(description, batchHost); 
                                    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();
                                    batchHost = CreateHostFromHeaders(this.dataService.Host, this.batchRequestStream, this.contentIds, changesetBoundary, writer);
                                    if (exceptionEncountered == null)
                                    { 
                                        this.batchRequestHost.Add(batchHost);
                                        this.host = batchHost; 
 
                                        this.dataService.InternalOnStartProcessingRequest(new ProcessRequestArgs(this.host.AbsoluteRequestUri, true /*isBatchOperation*/));
                                        description = RequestUriProcessor.ProcessRequestUri(this.host.AbsoluteRequestUri, this); 
                                        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 (this.batchRequestStream.State == BatchStreamState.Post && description != null)
                                        { 
                                            Debug.Assert(description.TargetKind == RequestTargetKind.Resource, "The target must be a resource, since otherwise cross-referencing doesn't make sense"); 

                                            // if the content id is specified, only then add it to the collection 
                                            if (batchHost.ContentId != null)
                                            {
                                                this.contentIdsToSegmentInfoMapping.Add(batchHost.ContentId, description.LastSegmentInfo);
                                            } 
                                        }
                                    } 
 
                                    break;
                                    #endregion // CUD Operation 
                                default:
                                    Debug.Assert(this.batchRequestStream.State == BatchStreamState.EndBatch, "expecting end batch state");
                                    break;
                            } 
                        }
                        catch (Exception exception) 
                        { 
                            if (!WebUtil.IsCatchableExceptionType(exception))
                            { 
                                throw;
                            }

                            if (this.batchRequestStream.State == BatchStreamState.EndChangeSet) 
                            {
                                this.HandleChangesetException(exception, this.batchRequestHost, 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
                            { 
                                BatchServiceHost currentHost = (BatchServiceHost)this.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 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.batchRequestHost.Clear();
                            }
                        } 
                    }
 
                    BatchWriter.WriteEndBoundary(writer, this.batchBoundary); 
                    writer.Flush();
                } 
                finally
                {
                    this.batchRequestStream.Dispose();
                } 
            }
 
            #region Private methods. 

            ///  
            /// Gets the value of the given header from the given header collection
            /// 
            /// Dictionary with header names and values.
            /// name of the header whose value needs to be returned. 
            /// value of the given header.
            private static string GetValue(Dictionary headers, string headerName) 
            { 
                string headerValue;
                headers.TryGetValue(headerName, out headerValue); 
                return headerValue;
            }

            ///  
            /// Creates a batch host from the given headers
            ///  
            /// IDataServiceHost implementation host for this data service. 
            /// batch stream which contains the header information.
            /// content ids that are defined in the batch. 
            /// Part separator for host.
            /// Output writer.
            /// instance of the batch host which represents the current operation.
            private static BatchServiceHost CreateHostFromHeaders(IDataServiceHost host, BatchStream batchStream, HashSet contentIds, string boundary, StreamWriter writer) 
            {
                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 = GetValue(batchStream.ContentHeaders, XmlConstants.HttpContentID);
                if (!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(contentId)); 
                    }
 
                    if (!contentIds.Add(contentId))
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.DataService_ContentIdMustBeUniqueInBatch(contentId));
                    } 
                }
 
                CachedRequestParams requestParams = CreateRequestParams(host, batchStream); 
                return new BatchServiceHost(requestParams, batchStream.GetContentStream(), contentIdValue, boundary, writer);
            } 

            /// 
            /// Creates a new instance of CachedRequestParams given the header information
            ///  
            /// IDataServiceHost implementation host for this data service.
            /// batch stream which contains the header information. 
            /// instance of the CachedRequestParams with all request header information. 
            private static CachedRequestParams CreateRequestParams(IDataServiceHost host, BatchStream batchStream)
            { 
                string accept = GetValue(batchStream.ContentHeaders, XmlConstants.HttpRequestAccept);
                string acceptCharset = GetValue(batchStream.ContentHeaders, XmlConstants.HttpRequestAcceptCharset);
                string contentType = GetValue(batchStream.ContentHeaders, XmlConstants.HttpContentType);
                string headerIfMatch = GetValue(batchStream.ContentHeaders, XmlConstants.HttpRequestIfMatch); 
                string headerIfNoneMatch = GetValue(batchStream.ContentHeaders, XmlConstants.HttpRequestIfNoneMatch);
                string version = GetValue(batchStream.ContentHeaders, XmlConstants.HttpDataServiceVersion); 
                string maxVersion = GetValue(batchStream.ContentHeaders, XmlConstants.HttpMaxDataServiceVersion); 
                Uri absoluteServiceUri = RequestUriProcessor.GetServiceUri(host);
                Uri contentUri = RequestUriProcessor.GetAbsoluteUriFromReference( 
                    batchStream.ContentUri,                             // reference
                    absoluteServiceUri);                                // absoluteServiceUri

                return new CachedRequestParams( 
                    accept,
                    acceptCharset, 
                    contentType, 
                    GetHttpMethodName(batchStream.State),
                    headerIfMatch, 
                    headerIfNoneMatch,
                    version,
                    maxVersion,
                    contentUri, 
                    absoluteServiceUri);
            } 
 
            /// 
            /// Returns the http method name given the batch stream state 
            /// 
            /// state of the batch stream.
            /// returns the http method name
            private static string GetHttpMethodName(BatchStreamState state) 
            {
                Debug.Assert( 
                    state == BatchStreamState.Get || 
                    state == BatchStreamState.Post ||
                    state == BatchStreamState.Put || 
                    state == BatchStreamState.Delete ||
                    state == BatchStreamState.Merge,
                    "Expecting BatchStreamState (" + state + ") to be Delete, Get, Post or Put");
 
                switch (state)
                { 
                    case BatchStreamState.Delete: 
                        return XmlConstants.HttpMethodDelete;
                    case BatchStreamState.Get: 
                        return XmlConstants.HttpMethodGet;
                    case BatchStreamState.Post:
                        return XmlConstants.HttpMethodPost;
                    case BatchStreamState.Merge: 
                        return XmlConstants.HttpMethodMerge;
                    default: 
                        Debug.Assert(BatchStreamState.Put == state, "BatchStreamState.Put == state"); 
                        return XmlConstants.HttpMethodPut;
                } 
            }

            /// 
            /// Gets the resource from the segment enumerable. 
            /// 
            /// segment from which resource needs to be returned. 
            /// returns the resource contained in the request enumerable. 
            private static object GetResourceFromSegmentEnumerable(SegmentInfo segmentInfo)
            { 
                Debug.Assert(segmentInfo.RequestEnumerable != null, "The segment should always have the result");
                object[] results = (object[])segmentInfo.RequestEnumerable;
                Debug.Assert(results != null && results.Length == 1, "results != null && results.Length == 1");
                Debug.Assert(results[0] != null, "results[0] != null"); 
                return results[0];
            } 
 
            /// 
            /// Write the exception encountered in the middle of the changeset to the response 
            /// 
            /// exception encountered
            /// list of hosts
            /// changeset boundary for the current processing changeset 
            /// writer to which the response needs to be written
            private void HandleChangesetException( 
                Exception exception, 
                List changesetHosts,
                string changesetBoundary, 
                StreamWriter writer)
            {
                Debug.Assert(exception != null, "exception != null");
                Debug.Assert(changesetHosts != null, "changesetHosts != 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 right the last operation information. 
                // If there are no host, then just pass null.
                BatchServiceHost currentHost = null;
                if (changesetHosts.Count == 0)
                { 
                    currentHost = new BatchServiceHost(changesetBoundary, writer);
                } 
                else 
                {
                    currentHost = changesetHosts[changesetHosts.Count - 1]; 
                }

                ErrorHandler.HandleBatchProcessException(this, currentHost, exception, writer);
 
                // Write end boundary for the changeset
                BatchWriter.WriteEndBoundary(writer, changesetBoundary); 
                this.dataService.Provider.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));
                } 
            }

            /// 
            /// 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, this.host, batchHost.BoundaryString); 
                } 
                else
                { 
                    Action responseWriter = DataService.SerializeResponseBody(description, this);
                    if (responseWriter != null)
                    {
                        BatchWriter.WriteBoundaryAndHeaders(batchHost.Writer, this.host, batchHost.BoundaryString); 
                        batchHost.Writer.Flush();
                        responseWriter(batchHost.Writer.BaseStream); 
                        batchHost.Writer.WriteLine(); 
                    }
                    else 
                    {
                        BatchWriter.WriteBoundaryAndHeaders(batchHost.Writer, this.host, batchHost.BoundaryString);
                    }
                } 
            }
 
            #endregion Private methods. 
        }
    } 
}

// 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