File: System\Data\Services\Client\DataServiceRequest.cs
Project: ndp\fx\src\DataWeb\Client\System.Data.Services.Client.csproj (System.Data.Services.Client)
//---------------------------------------------------------------------
// <copyright file="DataServiceRequest.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
// query request object
// </summary>
//---------------------------------------------------------------------
 
namespace System.Data.Services.Client
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Text;
#if !ASTORIA_LIGHT // Data.Services http stack
    using System.Net;
#else
    using System.Data.Services.Http;
#endif
    using System.Xml;
 
    /// <summary>non-generic placeholder for generic implementation</summary>
    public abstract class DataServiceRequest
    {
        /// <summary>internal constructor so that only our assembly can provide an implementation</summary>
        internal DataServiceRequest()
        {
        }
 
        /// <summary>Element Type</summary>
        public abstract Type ElementType
        {
            get;
        }
 
        /// <summary>Gets the URI for a the query</summary>
        public abstract Uri RequestUri
        {
            get;
        }
 
        /// <summary>The ProjectionPlan for the request, if precompiled in a previous page; null otherwise.</summary>
        internal abstract ProjectionPlan Plan
        {
            get;
        }
 
        /// <summary>The TranslateResult associated with this request</summary>
        internal abstract QueryComponents QueryComponents
        {
            get;
        }
 
        /// <summary>Gets the URI for a the query</summary>
        /// <returns>a string with the URI</returns>
        public override string ToString()
        {
            return this.QueryComponents.Uri.ToString();
        }   
 
        /// <summary>
        /// get an enumerable materializes the objects the response
        /// </summary>
        /// <param name="context">context</param>
        /// <param name="queryComponents">query components</param>
        /// <param name="plan">Projection plan (if compiled in an earlier query).</param>
        /// <param name="contentType">contentType</param>
        /// <param name="response">method to get http response stream</param>
        /// <returns>atom materializer</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Returning MaterializeAtom, caller will dispose")]
        internal static MaterializeAtom Materialize(DataServiceContext context, QueryComponents queryComponents, ProjectionPlan plan, string contentType, Stream response)
        {
            Debug.Assert(null != queryComponents, "querycomponents");
 
            string mime = null;
            Encoding encoding = null;
            if (!String.IsNullOrEmpty(contentType))
            {
                HttpProcessUtility.ReadContentType(contentType, out mime, out encoding);
            }
 
            if (String.Equals(mime, XmlConstants.MimeApplicationAtom, StringComparison.OrdinalIgnoreCase) ||
                String.Equals(mime, XmlConstants.MimeApplicationXml, StringComparison.OrdinalIgnoreCase))
            {
                if (null != response)
                {
                    XmlReader reader = XmlUtil.CreateXmlReader(response, encoding);
                    return new MaterializeAtom(context, reader, queryComponents, plan, context.MergeOption);
                }
            }
 
            return MaterializeAtom.EmptyResults;
        }
 
        /// <summary>
        /// Creates a instance of strongly typed DataServiceRequest with the given element type.
        /// </summary>
        /// <param name="elementType">element type for the DataServiceRequest.</param>
        /// <param name="requestUri">constructor parameter.</param>
        /// <returns>returns the strongly typed DataServiceRequest instance.</returns>
        internal static DataServiceRequest GetInstance(Type elementType, Uri requestUri)
        {
            Type genericType = typeof(DataServiceRequest<>).MakeGenericType(elementType);
            return (DataServiceRequest)Activator.CreateInstance(genericType, new object[] { requestUri });
        }
 
        /// <summary>
        /// Ends an asynchronous request to an Internet resource.
        /// </summary>
        /// <typeparam name="TElement">Element type of the result.</typeparam>
        /// <param name="source">Source object of async request.</param>
        /// <param name="context">The data service context.</param>
        /// <param name="asyncResult">The asyncResult being ended.</param>
        /// <returns>The response - result of the request.</returns>
        internal static IEnumerable<TElement> EndExecute<TElement>(object source, DataServiceContext context, IAsyncResult asyncResult)
        {
            QueryResult result = null;
            try
            {
                result = QueryResult.EndExecute<TElement>(source, asyncResult);
                return result.ProcessResult<TElement>(context, result.ServiceRequest.Plan);
            }
            catch (DataServiceQueryException ex)
            {
                Exception inEx = ex;
                while (inEx.InnerException != null)
                {
                    inEx = inEx.InnerException;
                }
 
                DataServiceClientException serviceEx = inEx as DataServiceClientException;
                if (context.IgnoreResourceNotFoundException && serviceEx != null && serviceEx.StatusCode == (int)HttpStatusCode.NotFound)
                {
                    QueryOperationResponse qor = new QueryOperationResponse<TElement>(new Dictionary<string, string>(ex.Response.Headers), ex.Response.Query, MaterializeAtom.EmptyResults);
                    qor.StatusCode = (int)HttpStatusCode.NotFound;
                    return (IEnumerable<TElement>)qor;
                }
 
                throw;
            }
        }
 
#if !ASTORIA_LIGHT // Synchronous methods not available
        /// <summary>
        /// execute uri and materialize result
        /// </summary>
        /// <typeparam name="TElement">element type</typeparam>
        /// <param name="context">context</param>
        /// <param name="queryComponents">query components for request to execute</param>
        /// <returns>enumerable of results</returns>
        internal QueryOperationResponse<TElement> Execute<TElement>(DataServiceContext context, QueryComponents queryComponents)
        {
            QueryResult result = null;
            try
            {
                DataServiceRequest<TElement> serviceRequest = new DataServiceRequest<TElement>(queryComponents, this.Plan);
                result = serviceRequest.CreateResult(this, context, null, null);
                result.Execute();
                return result.ProcessResult<TElement>(context, this.Plan);
            }
            catch (InvalidOperationException ex)
            {
                QueryOperationResponse operationResponse = result.GetResponse<TElement>(MaterializeAtom.EmptyResults);
 
                if (null != operationResponse)
                {
                    if (context.IgnoreResourceNotFoundException)
                    {
                        DataServiceClientException cex = ex as DataServiceClientException;
                        if (cex != null && cex.StatusCode == (int)HttpStatusCode.NotFound)
                        {
                            // don't throw
                            return (QueryOperationResponse<TElement>)operationResponse;
                        }
                    }
 
                    operationResponse.Error = ex;
                    throw new DataServiceQueryException(Strings.DataServiceException_GeneralError, ex, operationResponse);
                }
 
                throw;
            }
        }
 
        /// <summary>
        /// Synchronizely get the query set count from the server by executing the $count=value query
        /// </summary>
        /// <param name="context">The context</param>
        /// <returns>The server side count of the query set</returns>
        internal long GetQuerySetCount(DataServiceContext context)
        {
            Debug.Assert(null != context, "context is null");
            this.QueryComponents.Version = Util.DataServiceVersion2;
 
            QueryResult response = null;
            DataServiceRequest<long> serviceRequest = new DataServiceRequest<long>(this.QueryComponents, null);
            HttpWebRequest request = context.CreateRequest(this.QueryComponents.Uri, XmlConstants.HttpMethodGet, false, null, this.QueryComponents.Version, false);
            
            request.Accept = "text/plain";
            response = new QueryResult(this, "Execute", serviceRequest, request, null, null);
 
            try
            {
                response.Execute();
 
                if (HttpStatusCode.NoContent != response.StatusCode)
                {
                    StreamReader sr = new StreamReader(response.GetResponseStream());
                    long r = -1;
                    try
                    {
                        r = XmlConvert.ToInt64(sr.ReadToEnd());
                    }
                    finally
                    {
                        sr.Close();
                    }
 
                    return r;
                }
                else
                {
                    throw new DataServiceQueryException(Strings.DataServiceRequest_FailGetCount, response.Failure);
                }
            }
            catch (InvalidOperationException ex)
            {
                QueryOperationResponse operationResponse = null;
                operationResponse = response.GetResponse<long>(MaterializeAtom.EmptyResults);
                if (null != operationResponse)
                {
                    operationResponse.Error = ex;
                    throw new DataServiceQueryException(Strings.DataServiceException_GeneralError, ex, operationResponse);
                }
 
                throw;
            }
        }
#endif
 
        /// <summary>
        /// Begins an asynchronous request to an Internet resource.
        /// </summary>
        /// <param name="source">source of execute (DataServiceQuery or DataServiceContext</param>
        /// <param name="context">context</param>
        /// <param name="callback">The AsyncCallback delegate.</param>
        /// <param name="state">The state object for this request.</param>
        /// <returns>An IAsyncResult that references the asynchronous request for a response.</returns>
        internal IAsyncResult BeginExecute(object source, DataServiceContext context, AsyncCallback callback, object state)
        {
            QueryResult result = this.CreateResult(source, context, callback, state);
            result.BeginExecute();
            return result;
        }
 
        /// <summary>
        /// Creates the result object for the specified query parameters.
        /// </summary>
        /// <param name="source">The source object for the request.</param>
        /// <param name="context">The data service context.</param>
        /// <param name="callback">The AsyncCallback delegate.</param>
        /// <param name="state">The state object for the callback.</param>
        /// <returns>Result representing the create request. The request has not been initiated yet.</returns>
        private QueryResult CreateResult(object source, DataServiceContext context, AsyncCallback callback, object state)
        {
            Debug.Assert(null != context, "context is null");
            HttpWebRequest request = context.CreateRequest(this.QueryComponents.Uri, XmlConstants.HttpMethodGet, false, null, this.QueryComponents.Version, false);
            return new QueryResult(source, "Execute", this, request, callback, state);
        }
    }
}