536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
331 lines
10 KiB
C#
331 lines
10 KiB
C#
//---------------------------------------------------------------------
|
|
// <copyright file="ObjectQuery.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//
|
|
// @owner Microsoft
|
|
// @backupowner Microsoft
|
|
//---------------------------------------------------------------------
|
|
|
|
namespace System.Data.Objects
|
|
{
|
|
using System;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Data.Metadata.Edm;
|
|
using System.Data.Objects.Internal;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
|
|
/// <summary>
|
|
/// This class implements untyped queries at the object-layer.
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
|
|
public abstract class ObjectQuery : IEnumerable, IQueryable, IOrderedQueryable, IListSource
|
|
{
|
|
#region Private Instance Members
|
|
|
|
// -----------------
|
|
// Instance Fields
|
|
// -----------------
|
|
|
|
/// <summary>
|
|
/// The underlying implementation of this ObjectQuery as provided by a concrete subclass
|
|
/// of ObjectQueryImplementation. Implementations currently exist for Entity-SQL- and Linq-to-Entities-based ObjectQueries.
|
|
/// </summary>
|
|
private ObjectQueryState _state;
|
|
|
|
/// <summary>
|
|
/// The result type of the query - 'TResultType' expressed as an O-Space type usage. Cached here and
|
|
/// only instantiated if the <see cref="GetResultType"/> method is called.
|
|
/// </summary>
|
|
private TypeUsage _resultType;
|
|
|
|
/// <summary>
|
|
/// Every instance of ObjectQuery get a unique instance of the provider. This helps propagate state information
|
|
/// using the provider through LINQ operators.
|
|
/// </summary>
|
|
private IQueryProvider _provider;
|
|
|
|
#endregion
|
|
|
|
#region Internal Constructors
|
|
|
|
// --------------------
|
|
// Internal Constructors
|
|
// --------------------
|
|
|
|
/// <summary>
|
|
/// The common constructor.
|
|
/// </summary>
|
|
/// <param name="queryState">
|
|
/// The underlying implementation of this ObjectQuery
|
|
/// </param>
|
|
/// <returns>
|
|
/// A new ObjectQuery instance.
|
|
/// </returns>
|
|
internal ObjectQuery(ObjectQueryState queryState)
|
|
{
|
|
Debug.Assert(queryState != null, "ObjectQuery state cannot be null");
|
|
|
|
// Set the query state.
|
|
this._state = queryState;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal Properties
|
|
|
|
/// <summary>
|
|
/// Gets an untyped instantiation of the underlying ObjectQueryState that implements this ObjectQuery.
|
|
/// </summary>
|
|
internal ObjectQueryState QueryState { get { return this._state; } }
|
|
|
|
#endregion
|
|
|
|
#region IQueryable implementation
|
|
|
|
/// <summary>
|
|
/// Gets the result element type for this query instance.
|
|
/// </summary>
|
|
Type IQueryable.ElementType
|
|
{
|
|
get { return this._state.ElementType; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the expression describing this query. For queries built using
|
|
/// LINQ builder patterns, returns a full LINQ expression tree; otherwise,
|
|
/// returns a constant expression wrapping this query. Note that the
|
|
/// default expression is not cached. This allows us to differentiate
|
|
/// between LINQ and Entity-SQL queries.
|
|
/// </summary>
|
|
System.Linq.Expressions.Expression IQueryable.Expression
|
|
{
|
|
get
|
|
{
|
|
return this.GetExpression();
|
|
}
|
|
}
|
|
|
|
internal abstract System.Linq.Expressions.Expression GetExpression();
|
|
|
|
/// <summary>
|
|
/// Gets the IQueryProvider associated with this query instance.
|
|
/// </summary>
|
|
IQueryProvider IQueryable.Provider
|
|
{
|
|
get
|
|
{
|
|
if (_provider == null)
|
|
{
|
|
_provider = new System.Data.Objects.ELinq.ObjectQueryProvider(this);
|
|
}
|
|
return _provider;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
|
|
// ----------
|
|
// Properties
|
|
// ----------
|
|
|
|
// ----------------------
|
|
// IListSource Properties
|
|
// ----------------------
|
|
/// <summary>
|
|
/// IListSource.ContainsListCollection implementation. Always returns true.
|
|
/// </summary>
|
|
bool IListSource.ContainsListCollection
|
|
{
|
|
get
|
|
{
|
|
return false; // this means that the IList we return is the one which contains our actual data, it is not a collection
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the Command Text (if any) for this ObjectQuery.
|
|
/// </summary>
|
|
public string CommandText
|
|
{
|
|
get
|
|
{
|
|
string commandText;
|
|
if (!_state.TryGetCommandText(out commandText))
|
|
{
|
|
return String.Empty;
|
|
}
|
|
|
|
Debug.Assert(commandText != null && commandText.Length != 0, "Invalid Command Text returned");
|
|
return commandText;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The context for the query, which includes the connection, cache and
|
|
/// metadata. Note that only the connection property is mutable and must be
|
|
/// set before a query can be executed.
|
|
/// </summary>
|
|
public ObjectContext Context
|
|
{
|
|
get
|
|
{
|
|
return this._state.ObjectContext;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows optional control over how queried results interact with the object state manager.
|
|
/// </summary>
|
|
public MergeOption MergeOption
|
|
{
|
|
get
|
|
{
|
|
return this._state.EffectiveMergeOption;
|
|
}
|
|
|
|
set
|
|
{
|
|
EntityUtil.CheckArgumentMergeOption(value);
|
|
this._state.UserSpecifiedMergeOption = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The parameter collection for this query.
|
|
/// </summary>
|
|
public ObjectParameterCollection Parameters
|
|
{
|
|
get
|
|
{
|
|
return this._state.EnsureParameters();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines if the query plan should be cached.
|
|
/// </summary>
|
|
public bool EnablePlanCaching
|
|
{
|
|
get
|
|
{
|
|
return this._state.PlanCachingEnabled;
|
|
}
|
|
|
|
set
|
|
{
|
|
this._state.PlanCachingEnabled = value;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
// --------------
|
|
// Public Methods
|
|
// --------------
|
|
|
|
// ----------------------
|
|
// IListSource method
|
|
// ----------------------
|
|
/// <summary>
|
|
/// IListSource.GetList implementation
|
|
/// </summary>
|
|
/// <returns>
|
|
/// IList interface over the data to bind
|
|
/// </returns>
|
|
IList IListSource.GetList()
|
|
{
|
|
return this.GetIListSourceListInternal();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the provider-specific command text used to execute this query
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[Browsable(false)]
|
|
public string ToTraceString()
|
|
{
|
|
return this._state.GetExecutionPlan(null).ToTraceString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method returns information about the result type of the ObjectQuery.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// The TypeMetadata that describes the shape of the query results.
|
|
/// </returns>
|
|
public TypeUsage GetResultType ()
|
|
{
|
|
Context.EnsureMetadata();
|
|
if (null == this._resultType)
|
|
{
|
|
// Retrieve the result type from the implementation, in terms of C-Space.
|
|
TypeUsage cSpaceQueryResultType = this._state.ResultType;
|
|
|
|
// Determine the 'TResultType' equivalent type usage based on the mapped O-Space type.
|
|
// If the result type of the query is a collection[something], then
|
|
// extract out the 'something' (element type) and use that. This
|
|
// is the equivalent of saying the result type is T, rather than
|
|
// IEnumerable<T>, which aligns with users' expectations.
|
|
TypeUsage tResultType;
|
|
if (!TypeHelpers.TryGetCollectionElementType(cSpaceQueryResultType, out tResultType))
|
|
{
|
|
tResultType = cSpaceQueryResultType;
|
|
}
|
|
|
|
// Map the C-space result type to O-space.
|
|
tResultType = this._state.ObjectContext.Perspective.MetadataWorkspace.GetOSpaceTypeUsage(tResultType);
|
|
if (null == tResultType)
|
|
{
|
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectQuery_UnableToMapResultType);
|
|
}
|
|
|
|
this._resultType = tResultType;
|
|
}
|
|
|
|
return this._resultType;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method allows explicit query evaluation with a specified merge
|
|
/// option which will override the merge option property.
|
|
/// </summary>
|
|
/// <param name="mergeOption">
|
|
/// The MergeOption to use when executing the query.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An enumerable for the ObjectQuery results.
|
|
/// </returns>
|
|
public ObjectResult Execute(MergeOption mergeOption)
|
|
{
|
|
EntityUtil.CheckArgumentMergeOption(mergeOption);
|
|
return this.ExecuteInternal(mergeOption);
|
|
}
|
|
|
|
#region IEnumerable implementation
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return this.GetEnumeratorInternal();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Internal Methods
|
|
internal abstract IEnumerator GetEnumeratorInternal();
|
|
internal abstract IList GetIListSourceListInternal();
|
|
internal abstract ObjectResult ExecuteInternal(MergeOption mergeOption);
|
|
#endregion
|
|
}
|
|
}
|