File: System\Data\Services\Client\DataServiceQueryOfT.cs
Project: ndp\fx\src\DataWeb\Client\System.Data.Services.Client.csproj (System.Data.Services.Client)
//---------------------------------------------------------------------
// <copyright file="DataServiceQueryOfT.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
// query object
// </summary>
//---------------------------------------------------------------------
 
namespace System.Data.Services.Client
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Linq.Expressions;
#if !ASTORIA_LIGHT // Data.Services http stack
    using System.Net;
#else
    using System.Data.Services.Http;
#endif
    using System.Reflection;
    using System.Collections;
 
    /// <summary>
    /// query object
    /// </summary>
    /// <typeparam name="TElement">type of object to materialize</typeparam>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "required for this feature")]
    public class DataServiceQuery<TElement> : DataServiceQuery, IQueryable<TElement>
    {
        #region Private fields.
 
        /// <summary>Linq Expression</summary>
        private readonly Expression queryExpression;
 
        /// <summary>Linq Query Provider</summary>
        private readonly DataServiceQueryProvider queryProvider;
 
        /// <summary>Uri, Projection, Version for translated query</summary>
        private QueryComponents queryComponents;
 
        #endregion Private fields.
 
        /// <summary>
        /// query object
        /// </summary>
        /// <param name="expression">expression for query</param>
        /// <param name="provider">query provider for query</param>
        private DataServiceQuery(Expression expression, DataServiceQueryProvider provider)
        {
            Debug.Assert(null != provider.Context, "null context");
            Debug.Assert(expression != null, "null expression");
            Debug.Assert(provider is DataServiceQueryProvider, "Currently only support Web Query Provider");
 
            this.queryExpression = expression;
            this.queryProvider = provider;
        }
 
        #region IQueryable implementation
        /// <summary>
        /// Element Type
        /// </summary>
        public override Type ElementType
        {
            get { return typeof(TElement); }
        }
 
        /// <summary>
        /// Linq Expression
        /// </summary>
        public override Expression Expression
        {
            get { return this.queryExpression; }
        }
 
        /// <summary>
        /// Linq Query Provider
        /// </summary>
        public override IQueryProvider Provider
        {
            get { return this.queryProvider; }
        }
 
        #endregion
 
        /// <summary>
        /// gets the URI for a the query
        /// </summary>
        public override Uri RequestUri
        {
            get
            {
                return this.Translate().Uri;
            }
        }
 
        /// <summary>The ProjectionPlan for the request (if precompiled in a previous page).</summary>
        internal override ProjectionPlan Plan
        {
            get { return null; }
        }
 
        /// <summary>
        /// gets the UriTranslateResult for a the query
        /// </summary>
        internal override QueryComponents QueryComponents
        {
            get
            {
                return this.Translate();
            }
        }
 
        /// <summary>
        /// Begins an asynchronous request to an Internet resource.
        /// </summary>
        /// <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>
        public new IAsyncResult BeginExecute(AsyncCallback callback, object state)
        {
            return base.BeginExecute(this, this.queryProvider.Context, callback, state);
        }
 
        /// <summary>
        /// Ends an asynchronous request to an Internet resource.
        /// </summary>
        /// <param name="asyncResult">The pending request for a response. </param>
        /// <returns>An IEnumerable that contains the response from the Internet resource.</returns>
        public new IEnumerable<TElement> EndExecute(IAsyncResult asyncResult)
        {
            return DataServiceRequest.EndExecute<TElement>(this, this.queryProvider.Context, asyncResult);
        }
 
#if !ASTORIA_LIGHT // Synchronous methods not available
        /// <summary>
        /// Returns an IEnumerable from an Internet resource. 
        /// </summary>
        /// <returns>An IEnumerable that contains the response from the Internet resource.</returns>
        public new IEnumerable<TElement> Execute()
        {
            return this.Execute<TElement>(this.queryProvider.Context, this.Translate());
        }
#endif
 
        /// <summary>
        /// Sets Expand option
        /// </summary>
        /// <param name="path">Path to expand</param>
        /// <returns>New DataServiceQuery with expand option</returns>
        public DataServiceQuery<TElement> Expand(string path)
        {
            Util.CheckArgumentNull(path, "path");
            Util.CheckArgumentNotEmpty(path, "path");
 
            MethodInfo mi = typeof(DataServiceQuery<TElement>).GetMethod("Expand");
            return (DataServiceQuery<TElement>)this.Provider.CreateQuery<TElement>(
                Expression.Call(
                    Expression.Convert(this.Expression, typeof(DataServiceQuery<TElement>.DataServiceOrderedQuery)),
                    mi,
                    new Expression[] { Expression.Constant(path) }));
        }
 
        /// <summary>
        /// Turn the inline counting option on for the query
        /// </summary>
        /// <returns>New DataServiceQuery with count option</returns>
        public DataServiceQuery<TElement> IncludeTotalCount()
        {
            MethodInfo mi = typeof(DataServiceQuery<TElement>).GetMethod("IncludeTotalCount");
            
            return (DataServiceQuery<TElement>)this.Provider.CreateQuery<TElement>(
                Expression.Call(
                    Expression.Convert(this.Expression, typeof(DataServiceQuery<TElement>.DataServiceOrderedQuery)),
                    mi));
        }
 
        /// <summary>
        /// Sets user option
        /// </summary>
        /// <param name="name">name of value</param>
        /// <param name="value">value of option</param>
        /// <returns>New DataServiceQuery with expand option</returns>
        public DataServiceQuery<TElement> AddQueryOption(string name, object value)
        {
            Util.CheckArgumentNull(name, "name");
            Util.CheckArgumentNull(value, "value");
            MethodInfo mi = typeof(DataServiceQuery<TElement>).GetMethod("AddQueryOption");
            return (DataServiceQuery<TElement>)this.Provider.CreateQuery<TElement>(
                Expression.Call(
                    Expression.Convert(this.Expression, typeof(DataServiceQuery<TElement>.DataServiceOrderedQuery)),
                    mi,
                    new Expression[] { Expression.Constant(name), Expression.Constant(value, typeof(object)) }));
        }
 
        /// <summary>
        /// get an enumerator materializes the objects the Uri request
        /// </summary>
        /// <returns>an enumerator</returns>
#if !ASTORIA_LIGHT // Synchronous methods not available
        public IEnumerator<TElement> GetEnumerator()
        {
            return this.Execute().GetEnumerator();            
        }
#else
        IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator()
        {
            throw Error.NotSupported(Strings.DataServiceQuery_EnumerationNotSupportedInSL);
        }
#endif
 
        /// <summary>
        /// gets the URI for the query
        /// </summary>
        /// <returns>a string with the URI</returns>
        public override string ToString()
        {
            try
            {
                return base.ToString();
            }
            catch (NotSupportedException e)
            {
                return Strings.ALinq_TranslationError(e.Message);
            }
        }
 
        /// <summary>
        /// get an enumerator materializes the objects the Uri request
        /// </summary>
        /// <returns>an enumerator</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
#if !ASTORIA_LIGHT // Synchronous methods not available
            return this.GetEnumerator();
#else
            throw Error.NotSupported();
#endif
        }
 
#if !ASTORIA_LIGHT
        /// Synchronous methods not available
        /// <summary>
        /// Returns an IEnumerable from an Internet resource. 
        /// </summary>
        /// <returns>An IEnumerable that contains the response from the Internet resource.</returns>
        internal override IEnumerable ExecuteInternal()
        {
            return this.Execute();
        }
#endif
 
        /// <summary>
        /// Begins an asynchronous request to an Internet resource.
        /// </summary>
        /// <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 override IAsyncResult BeginExecuteInternal(AsyncCallback callback, object state)
        {
            return this.BeginExecute(callback, state);
        }
 
        /// <summary>
        /// Ends an asynchronous request to an Internet resource.
        /// </summary>
        /// <param name="asyncResult">The pending request for a response. </param>
        /// <returns>An IEnumerable that contains the response from the Internet resource.</returns>
        internal override IEnumerable EndExecuteInternal(IAsyncResult asyncResult)
        {
            return this.EndExecute(asyncResult);
        }
 
        /// <summary>
        /// gets the query components for the query after translating
        /// </summary>
        /// <returns>QueryComponents for query</returns>
        private QueryComponents Translate()
        {
            if (this.queryComponents == null)
            {
                this.queryComponents = this.queryProvider.Translate(this.queryExpression);
            }
 
            return this.queryComponents;
        }
 
        /// <summary>
        /// Ordered DataServiceQuery which implements IOrderedQueryable.
        /// </summary>
        internal class DataServiceOrderedQuery : DataServiceQuery<TElement>, IOrderedQueryable<TElement>, IOrderedQueryable
        {
            /// <summary>
            /// constructor
            /// </summary>
            /// <param name="expression">expression for query</param>
            /// <param name="provider">query provider for query</param>
            internal DataServiceOrderedQuery(Expression expression, DataServiceQueryProvider provider)
                : base(expression, provider)
            {
            }
        }
    }
}