Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / ObjectQuery.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ObjectQuery.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupowner Microsoft
8 //---------------------------------------------------------------------
9
10 namespace System.Data.Objects
11 {
12     using System;
13     using System.Collections;
14     using System.ComponentModel;
15     using System.Data;
16     using System.Data.Common;
17     using System.Data.Metadata.Edm;
18     using System.Data.Objects.Internal;
19     using System.Diagnostics;
20     using System.Linq;
21
22     /// <summary>
23     ///   This class implements untyped queries at the object-layer. 
24     /// </summary>
25     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
26     public abstract class ObjectQuery : IEnumerable, IQueryable, IOrderedQueryable, IListSource
27     {
28         #region Private Instance Members
29
30         // -----------------
31         // Instance Fields
32         // -----------------
33         
34         /// <summary>
35         ///   The underlying implementation of this ObjectQuery as provided by a concrete subclass
36         ///   of ObjectQueryImplementation. Implementations currently exist for Entity-SQL- and Linq-to-Entities-based ObjectQueries.
37         /// </summary>
38         private ObjectQueryState _state;
39
40         /// <summary>
41         ///   The result type of the query - 'TResultType' expressed as an O-Space type usage. Cached here and
42         ///   only instantiated if the <see cref="GetResultType"/> method is called.
43         /// </summary>
44         private TypeUsage _resultType;
45
46         /// <summary>
47         /// Every instance of ObjectQuery get a unique instance of the provider. This helps propagate state information
48         /// using the provider through LINQ operators.
49         /// </summary>
50         private IQueryProvider _provider;
51
52         #endregion
53
54         #region Internal Constructors
55
56         // --------------------
57         // Internal Constructors
58         // --------------------
59
60         /// <summary>
61         ///   The common constructor.
62         /// </summary>
63         /// <param name="queryState">
64         ///   The underlying implementation of this ObjectQuery
65         /// </param>
66         /// <returns>
67         ///   A new ObjectQuery instance.
68         /// </returns>
69         internal ObjectQuery(ObjectQueryState queryState)
70         {
71             Debug.Assert(queryState != null, "ObjectQuery state cannot be null");
72             
73             // Set the query state.
74             this._state = queryState;
75         }
76                 
77         #endregion
78
79         #region Internal Properties
80
81         /// <summary>
82         /// Gets an untyped instantiation of the underlying ObjectQueryState that implements this ObjectQuery.
83         /// </summary>
84         internal ObjectQueryState QueryState { get { return this._state; } }
85                 
86         #endregion
87
88         #region IQueryable implementation
89
90         /// <summary>
91         /// Gets the result element type for this query instance.
92         /// </summary>
93         Type IQueryable.ElementType
94         {
95             get { return this._state.ElementType; }
96         }
97
98         /// <summary>
99         /// Gets the expression describing this query. For queries built using
100         /// LINQ builder patterns, returns a full LINQ expression tree; otherwise,
101         /// returns a constant expression wrapping this query. Note that the
102         /// default expression is not cached. This allows us to differentiate
103         /// between LINQ and Entity-SQL queries.
104         /// </summary>
105         System.Linq.Expressions.Expression IQueryable.Expression
106         {
107             get
108             {
109                 return this.GetExpression();
110             }
111         }
112
113         internal abstract System.Linq.Expressions.Expression GetExpression();
114         
115         /// <summary>
116         /// Gets the IQueryProvider associated with this query instance.
117         /// </summary>
118         IQueryProvider IQueryable.Provider
119         {
120             get
121             {
122                 if (_provider == null)
123                 {
124                     _provider = new System.Data.Objects.ELinq.ObjectQueryProvider(this);
125                 }
126                 return _provider;
127             }
128         }
129
130         #endregion
131
132         #region Public Properties
133
134         // ----------
135         // Properties
136         // ----------
137
138         // ----------------------
139         // IListSource  Properties
140         // ----------------------
141         /// <summary>
142         ///   IListSource.ContainsListCollection implementation. Always returns true.
143         /// </summary>
144         bool IListSource.ContainsListCollection
145         {
146             get
147             {
148                 return false; // this means that the IList we return is the one which contains our actual data, it is not a collection
149             }
150         }
151
152         /// <summary>
153         /// Gets the Command Text (if any) for this ObjectQuery.
154         /// </summary>
155         public string CommandText
156         {
157             get
158             {
159                 string commandText;
160                 if (!_state.TryGetCommandText(out commandText))
161                 {
162                     return String.Empty;
163                 }
164
165                 Debug.Assert(commandText != null && commandText.Length != 0, "Invalid Command Text returned");
166                 return commandText;
167             }
168         }
169
170         /// <summary>
171         ///   The context for the query, which includes the connection, cache and
172         ///   metadata. Note that only the connection property is mutable and must be
173         ///   set before a query can be executed.
174         /// </summary>
175         public ObjectContext Context
176         {
177             get
178             {
179                 return this._state.ObjectContext;
180             }
181         }
182
183         /// <summary>
184         ///   Allows optional control over how queried results interact with the object state manager.
185         /// </summary>
186         public MergeOption MergeOption
187         {
188             get
189             {
190                 return this._state.EffectiveMergeOption;
191             }
192
193             set
194             {
195                 EntityUtil.CheckArgumentMergeOption(value);
196                 this._state.UserSpecifiedMergeOption = value;
197             }
198         }
199
200         /// <summary>
201         ///   The parameter collection for this query.
202         /// </summary>
203         public ObjectParameterCollection Parameters
204         {
205             get
206             {
207                 return this._state.EnsureParameters();
208             }
209         }
210
211         /// <summary>
212         ///   Defines if the query plan should be cached.
213         /// </summary>
214         public bool EnablePlanCaching
215         {
216             get
217             {
218                 return this._state.PlanCachingEnabled;
219             }
220
221             set
222             {
223                 this._state.PlanCachingEnabled = value;
224             }
225         }
226
227         #endregion
228
229         #region Public Methods
230
231         // --------------
232         // Public Methods
233         // --------------
234
235         // ----------------------
236         // IListSource  method
237         // ----------------------
238         /// <summary>
239         ///   IListSource.GetList implementation
240         /// </summary>
241         /// <returns>
242         ///   IList interface over the data to bind
243         /// </returns>
244         IList IListSource.GetList()
245         {
246             return this.GetIListSourceListInternal();
247         }
248
249         /// <summary>
250         ///   Get the provider-specific command text used to execute this query
251         /// </summary>
252         /// <returns></returns>
253         [Browsable(false)]
254         public string ToTraceString() 
255         {
256             return this._state.GetExecutionPlan(null).ToTraceString();
257         }
258                         
259         /// <summary>
260         ///   This method returns information about the result type of the ObjectQuery.
261         /// </summary>
262         /// <returns>
263         ///   The TypeMetadata that describes the shape of the query results.
264         /// </returns>
265         public TypeUsage GetResultType ()
266         {
267             Context.EnsureMetadata();
268             if (null == this._resultType)
269             {
270                 // Retrieve the result type from the implementation, in terms of C-Space.
271                 TypeUsage cSpaceQueryResultType = this._state.ResultType;
272                 
273                 // Determine the 'TResultType' equivalent type usage based on the mapped O-Space type.
274                 // If the result type of the query is a collection[something], then
275                 // extract out the 'something' (element type) and use that. This
276                 // is the equivalent of saying the result type is T, rather than
277                 // IEnumerable<T>, which aligns with users' expectations.
278                 TypeUsage tResultType;
279                 if (!TypeHelpers.TryGetCollectionElementType(cSpaceQueryResultType, out tResultType))
280                 {
281                     tResultType = cSpaceQueryResultType;
282                 }
283
284                 // Map the C-space result type to O-space.
285                 tResultType = this._state.ObjectContext.Perspective.MetadataWorkspace.GetOSpaceTypeUsage(tResultType);
286                 if (null == tResultType)
287                 {
288                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectQuery_UnableToMapResultType);
289                 }
290
291                 this._resultType = tResultType;
292             }
293
294             return this._resultType;
295         }
296          
297         /// <summary>
298         ///   This method allows explicit query evaluation with a specified merge
299         ///   option which will override the merge option property.
300         /// </summary>
301         /// <param name="mergeOption">
302         ///   The MergeOption to use when executing the query.
303         /// </param>
304         /// <returns>
305         ///   An enumerable for the ObjectQuery results.
306         /// </returns>
307         public ObjectResult Execute(MergeOption mergeOption)
308         {
309             EntityUtil.CheckArgumentMergeOption(mergeOption);
310             return this.ExecuteInternal(mergeOption);
311         }
312                 
313         #region IEnumerable implementation
314
315         IEnumerator IEnumerable.GetEnumerator()
316         {
317             return this.GetEnumeratorInternal();
318         }
319
320         #endregion
321
322         #endregion
323
324         #region Internal Methods
325         internal abstract IEnumerator GetEnumeratorInternal();
326         internal abstract IList GetIListSourceListInternal();
327         internal abstract ObjectResult ExecuteInternal(MergeOption mergeOption);
328         #endregion
329     }
330 }