1 //---------------------------------------------------------------------
2 // <copyright file="ObjectQuery.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupowner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Objects
13 using System.Collections;
14 using System.ComponentModel;
16 using System.Data.Common;
17 using System.Data.Metadata.Edm;
18 using System.Data.Objects.Internal;
19 using System.Diagnostics;
23 /// This class implements untyped queries at the object-layer.
25 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
26 public abstract class ObjectQuery : IEnumerable, IQueryable, IOrderedQueryable, IListSource
28 #region Private Instance Members
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.
38 private ObjectQueryState _state;
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.
44 private TypeUsage _resultType;
47 /// Every instance of ObjectQuery get a unique instance of the provider. This helps propagate state information
48 /// using the provider through LINQ operators.
50 private IQueryProvider _provider;
54 #region Internal Constructors
56 // --------------------
57 // Internal Constructors
58 // --------------------
61 /// The common constructor.
63 /// <param name="queryState">
64 /// The underlying implementation of this ObjectQuery
67 /// A new ObjectQuery instance.
69 internal ObjectQuery(ObjectQueryState queryState)
71 Debug.Assert(queryState != null, "ObjectQuery state cannot be null");
73 // Set the query state.
74 this._state = queryState;
79 #region Internal Properties
82 /// Gets an untyped instantiation of the underlying ObjectQueryState that implements this ObjectQuery.
84 internal ObjectQueryState QueryState { get { return this._state; } }
88 #region IQueryable implementation
91 /// Gets the result element type for this query instance.
93 Type IQueryable.ElementType
95 get { return this._state.ElementType; }
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.
105 System.Linq.Expressions.Expression IQueryable.Expression
109 return this.GetExpression();
113 internal abstract System.Linq.Expressions.Expression GetExpression();
116 /// Gets the IQueryProvider associated with this query instance.
118 IQueryProvider IQueryable.Provider
122 if (_provider == null)
124 _provider = new System.Data.Objects.ELinq.ObjectQueryProvider(this);
132 #region Public Properties
138 // ----------------------
139 // IListSource Properties
140 // ----------------------
142 /// IListSource.ContainsListCollection implementation. Always returns true.
144 bool IListSource.ContainsListCollection
148 return false; // this means that the IList we return is the one which contains our actual data, it is not a collection
153 /// Gets the Command Text (if any) for this ObjectQuery.
155 public string CommandText
160 if (!_state.TryGetCommandText(out commandText))
165 Debug.Assert(commandText != null && commandText.Length != 0, "Invalid Command Text returned");
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.
175 public ObjectContext Context
179 return this._state.ObjectContext;
184 /// Allows optional control over how queried results interact with the object state manager.
186 public MergeOption MergeOption
190 return this._state.EffectiveMergeOption;
195 EntityUtil.CheckArgumentMergeOption(value);
196 this._state.UserSpecifiedMergeOption = value;
201 /// The parameter collection for this query.
203 public ObjectParameterCollection Parameters
207 return this._state.EnsureParameters();
212 /// Defines if the query plan should be cached.
214 public bool EnablePlanCaching
218 return this._state.PlanCachingEnabled;
223 this._state.PlanCachingEnabled = value;
229 #region Public Methods
235 // ----------------------
236 // IListSource method
237 // ----------------------
239 /// IListSource.GetList implementation
242 /// IList interface over the data to bind
244 IList IListSource.GetList()
246 return this.GetIListSourceListInternal();
250 /// Get the provider-specific command text used to execute this query
252 /// <returns></returns>
254 public string ToTraceString()
256 return this._state.GetExecutionPlan(null).ToTraceString();
260 /// This method returns information about the result type of the ObjectQuery.
263 /// The TypeMetadata that describes the shape of the query results.
265 public TypeUsage GetResultType ()
267 Context.EnsureMetadata();
268 if (null == this._resultType)
270 // Retrieve the result type from the implementation, in terms of C-Space.
271 TypeUsage cSpaceQueryResultType = this._state.ResultType;
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))
281 tResultType = cSpaceQueryResultType;
284 // Map the C-space result type to O-space.
285 tResultType = this._state.ObjectContext.Perspective.MetadataWorkspace.GetOSpaceTypeUsage(tResultType);
286 if (null == tResultType)
288 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectQuery_UnableToMapResultType);
291 this._resultType = tResultType;
294 return this._resultType;
298 /// This method allows explicit query evaluation with a specified merge
299 /// option which will override the merge option property.
301 /// <param name="mergeOption">
302 /// The MergeOption to use when executing the query.
305 /// An enumerable for the ObjectQuery results.
307 public ObjectResult Execute(MergeOption mergeOption)
309 EntityUtil.CheckArgumentMergeOption(mergeOption);
310 return this.ExecuteInternal(mergeOption);
313 #region IEnumerable implementation
315 IEnumerator IEnumerable.GetEnumerator()
317 return this.GetEnumeratorInternal();
324 #region Internal Methods
325 internal abstract IEnumerator GetEnumeratorInternal();
326 internal abstract IList GetIListSourceListInternal();
327 internal abstract ObjectResult ExecuteInternal(MergeOption mergeOption);