DataService.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataWeb / Server / System / Data / Services / DataService.cs / 2 / 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)
            {
                // This if expression was missing from V1.0, but is a breaking change to add it
                // without also checking for the new OverrideEntitySetRights 
                if ((description.LastSegmentInfo.Operation != null) &&
                    (0 != (description.LastSegmentInfo.Operation.Rights & ServiceOperationRights.OverrideEntitySetRights))) 
                { 
                    dataService.Configuration.CheckServiceRights(description.LastSegmentInfo.Operation, description.IsSingleResult);
                } 
                else
                {
                    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);

                // We should only write etag in the response, if the type has one or more etag properties defined.
                // WriteETagValueInResponseHeader checks for null etag value (which means that no etag properties are defined) 
                // that before calling the host.
                WriteETagValueInResponseHeader(description, WebUtil.GetETagValue(dataService.Provider, actualEntity, container), dataService.Host); 
                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)
            {
                // This if expression was missing from V1.0, but is a breaking change to add it
                // without also checking for the new OverrideEntitySetRights 
                if ((description.LastSegmentInfo.Operation != null) &&
                    (0 != (description.LastSegmentInfo.Operation.Rights & ServiceOperationRights.OverrideEntitySetRights))) 
                { 
                    dataService.Configuration.CheckServiceRights(description.LastSegmentInfo.Operation, description.IsSingleResult);
                } 
                else
                {
                    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);

                // We should only write etag in the response, if the type has one or more etag properties defined.
                // WriteETagValueInResponseHeader checks for null etag value (which means that no etag properties are defined) 
                // that before calling the host.
                WriteETagValueInResponseHeader(description, WebUtil.GetETagValue(dataService.Provider, actualEntity, container), dataService.Host); 
                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