Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / EntityClient / EntityCommandDefinition.cs / 1305376 / EntityCommandDefinition.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//-----------------------------------------------------------------------------
namespace System.Data.EntityClient {
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Common;
using System.Data.Common.CommandTrees;
using System.Data.Query.InternalTrees;
using System.Data.Metadata.Edm;
using System.Data.Query.ResultAssembly;
using System.Data.Query.PlanCompiler;
using System.Diagnostics;
using System.Data.Common.Utils;
using System.Text;
using System.Data.Mapping;
///
/// An aggregate Command Definition used by the EntityClient layers. This is an aggregator
/// object that represent information from multiple underlying provider commands.
///
sealed internal class EntityCommandDefinition : DbCommandDefinition {
#region internal state
///
/// Bid Counter object
///
private static int _objectTypeCount;
///
/// The object Id of this instance, for BID tracing.
///
internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
///
/// nested store command definitions
///
private readonly List _mappedCommandDefinitions;
///
/// generates column map for the store result reader
///
private readonly IColumnMapGenerator _columnMapGenerator;
///
/// list of the parameters that the resulting command should have
///
private readonly System.Collections.ObjectModel.ReadOnlyCollection _parameters;
///
/// Set of entity sets exposed in the command.
///
private readonly Set _entitySets;
#endregion
#region constructors
///
/// don't let this be constructed publicly;
///
/// Cannot prepare the command definition for execution; consult the InnerException for more information.
/// The ADO.NET Data Provider you are using does not support CommandTrees.
internal EntityCommandDefinition(DbProviderFactory storeProviderFactory, DbCommandTree commandTree) {
EntityUtil.CheckArgumentNull(storeProviderFactory, "storeProviderFactory");
EntityUtil.CheckArgumentNull(commandTree, "commandTree");
EntityBid.Trace(" %d# Constructing. commandTree=%d#\n", ObjectID, commandTree.ObjectId);
DbProviderServices storeProviderServices = DbProviderServices.GetProviderServices(storeProviderFactory);
try {
if (DbCommandTreeKind.Query == commandTree.CommandTreeKind) {
// Next compile the plan for the command tree
List mappedCommandList = new List();
ColumnMap columnMap;
int columnCount;
PlanCompiler.Compile(commandTree, out mappedCommandList, out columnMap, out columnCount, out _entitySets);
_columnMapGenerator = new ConstantColumnMapGenerator(columnMap, columnCount);
// Note: we presume that the first item in the ProviderCommandInfo is the root node;
Debug.Assert(mappedCommandList.Count > 0, "empty providerCommandInfo collection and no exception?"); // this shouldn't ever happen.
// Then, generate the store commands from the resulting command tree(s)
_mappedCommandDefinitions = new List(mappedCommandList.Count);
foreach (ProviderCommandInfo providerCommandInfo in mappedCommandList) {
DbCommandDefinition providerCommandDefinition = storeProviderServices.CreateCommandDefinition(providerCommandInfo.CommandTree);
if (null == providerCommandDefinition) {
throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.ProviderReturnedNullForCreateCommandDefinition);
}
_mappedCommandDefinitions.Add(providerCommandDefinition);
}
}
else {
Debug.Assert(DbCommandTreeKind.Function == commandTree.CommandTreeKind, "only query and function command trees are supported");
DbFunctionCommandTree entityCommandTree = (DbFunctionCommandTree)commandTree;
// Retrieve mapping and metadata information for the function import.
FunctionImportMapping mapping = GetTargetFunctionMapping(entityCommandTree);
TypeUsage storeResultType = DetermineStoreResultType(entityCommandTree.MetadataWorkspace, mapping, out _columnMapGenerator);
// Copy over parameters (this happens through a more indirect route in the plan compiler, but
// it happens nonetheless)
List> providerParameters = new List>();
foreach (KeyValuePair parameter in entityCommandTree.Parameters)
{
providerParameters.Add(parameter);
}
// Construct store command tree usage.
DbFunctionCommandTree providerCommandTree = new DbFunctionCommandTree(entityCommandTree.MetadataWorkspace, DataSpace.SSpace,
mapping.TargetFunction, storeResultType, providerParameters);
DbCommandDefinition storeCommandDefinition = storeProviderServices.CreateCommandDefinition(providerCommandTree);
_mappedCommandDefinitions = new List(1) { storeCommandDefinition };
if (null != mapping.FunctionImport.EntitySet) {
_entitySets = new Set();
_entitySets.Add(mapping.FunctionImport.EntitySet);
_entitySets.MakeReadOnly();
}
}
// Finally, build a list of the parameters that the resulting command should have;
List parameterList = new List();
foreach (KeyValuePair queryParameter in commandTree.Parameters) {
EntityParameter parameter = CreateEntityParameterFromQueryParameter(queryParameter);
parameterList.Add(parameter);
}
_parameters = new System.Collections.ObjectModel.ReadOnlyCollection(parameterList);
}
catch (EntityCommandCompilationException) {
// No need to re-wrap EntityCommandCompilationException
throw;
}
catch (Exception e) {
// we should not be wrapping all exceptions
if (EntityUtil.IsCatchableExceptionType(e)) {
// we don't wan't folks to have to know all the various types of exceptions that can
// occur, so we just rethrow a CommandDefinitionException and make whatever we caught
// the inner exception of it.
throw EntityUtil.CommandCompilation(System.Data.Entity.Strings.EntityClient_CommandDefinitionPreparationFailed, e);
}
throw;
}
}
///
/// Determines the store type for a function import.
///
private TypeUsage DetermineStoreResultType(MetadataWorkspace workspace, FunctionImportMapping mapping, out IColumnMapGenerator columnMapGenerator) {
// Determine column maps and infer result types for the mapped function. There are four varieties:
// Collection(Entity)
// Collection(PrimitiveType)
// Collection(ComplexType)
// No result type
TypeUsage storeResultType;
{
StructuralType baseStructuralType;
EdmFunction functionImport = mapping.FunctionImport;
// Collection(Entity) or Collection(ComplexType)
if (MetadataHelper.TryGetFunctionImportReturnType(functionImport, out baseStructuralType))
{
ValidateEdmResultType(baseStructuralType, functionImport);
EntitySet entitySet = functionImport.EntitySet;
columnMapGenerator = new FunctionColumnMapGenerator(mapping, entitySet, baseStructuralType);
// We don't actually know the return type for the stored procedure, but we can infer
// one based on the mapping (i.e.: a column for every property of the mapped types
// and for all discriminator columns)
storeResultType = mapping.GetExpectedTargetResultType(workspace);
}
// Collection(PrimitiveType)
else if (functionImport.ReturnParameter != null && functionImport.ReturnParameter.TypeUsage != null) {
// Get components of metadata description
storeResultType = functionImport.ReturnParameter.TypeUsage;
Debug.Assert(storeResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType, "FunctionImport currently supports only collection result type");
TypeUsage elementType = ((CollectionType)storeResultType.EdmType).TypeUsage;
Debug.Assert(elementType.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType, "FunctionImport supports only Collection(Entity) and Collection(Primitive)");
// Build collection column map where the first column of the store result is assumed
// to contain the primitive type values.
ScalarColumnMap scalarColumnMap = new ScalarColumnMap(elementType, string.Empty, 0, 0);
SimpleCollectionColumnMap collectionColumnMap = new SimpleCollectionColumnMap(storeResultType,
string.Empty, scalarColumnMap, null, null);
columnMapGenerator = new ConstantColumnMapGenerator(collectionColumnMap, 1);
}
// No result type
else {
storeResultType = null;
columnMapGenerator = new ConstantColumnMapGenerator(null, 0);
}
}
return storeResultType;
}
///
/// Handles the following negative scenarios
/// Nested ComplexType Property in ComplexType
///
///
private void ValidateEdmResultType(EdmType resultType, EdmFunction functionImport)
{
if (Helper.IsComplexType(resultType))
{
ComplexType complexType = resultType as ComplexType;
Debug.Assert(null != complexType, "we should have a complex type here");
foreach (var property in complexType.Properties)
{
if (property.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType)
{
throw new NotSupportedException(System.Data.Entity.Strings.ComplexTypeAsReturnTypeAndNestedComplexProperty(property.Name, complexType.Name, functionImport.FullName));
}
}
}
}
///
/// Retrieves mapping for the given C-Space functionCommandTree
///
private static FunctionImportMapping GetTargetFunctionMapping(DbFunctionCommandTree functionCommandTree) {
Debug.Assert(functionCommandTree.DataSpace == DataSpace.CSpace, "map from CSpace->SSpace function");
Debug.Assert(null != functionCommandTree, "null functionCommandTree");
// find mapped store function
FunctionImportMapping targetFunctionMapping;
if (!functionCommandTree.MetadataWorkspace.TryGetFunctionImportMapping(functionCommandTree.EdmFunction, out targetFunctionMapping)) {
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnmappedFunctionImport);
}
return targetFunctionMapping;
}
#endregion
#region public API
///
/// Create a DbCommand object from the definition, that can be executed
///
///
public override DbCommand CreateCommand() {
EntityBid.Trace(" %d#\n", ObjectID);
return new EntityCommand(this);
}
#endregion
#region internal methods
///
/// Get a list of commands to be executed by the provider
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal IEnumerable MappedCommands {
get {
// Build up the list of command texts, if we haven't done so yet
List mappedCommandTexts = new List();
foreach (DbCommandDefinition commandDefinition in _mappedCommandDefinitions) {
DbCommand mappedCommand = commandDefinition.CreateCommand();
mappedCommandTexts.Add(mappedCommand.CommandText);
}
return mappedCommandTexts;
}
}
///
/// Creates ColumnMap for result assembly using the given reader.
///
internal ColumnMap CreateColumnMap(DbDataReader storeDataReader) {
return _columnMapGenerator.CreateColumnMap(storeDataReader);
}
///
/// Property to expose the known parameters for the query, so the Command objects
/// constructor can poplulate it's parameter collection from.
///
internal IEnumerable Parameters {
get {
return _parameters;
}
}
///
/// Set of entity sets exposed in the command.
///
internal Set EntitySets {
get {
return _entitySets;
}
}
///
/// Constructs a EntityParameter from a CQT parameter.
///
///
///
private static EntityParameter CreateEntityParameterFromQueryParameter(KeyValuePair queryParameter) {
// We really can't have a parameter here that isn't a scalar type...
Debug.Assert(TypeSemantics.IsPrimitiveType(queryParameter.Value), "Non-PrimitiveType used as query parameter type");
EntityParameter result = new EntityParameter();
result.ParameterName = queryParameter.Key;
DbCommandDefinition.PopulateParameterFromTypeUsage(result, queryParameter.Value, false /* isOutParam */ );
return result;
}
///
/// Internal execute method -- copies command information from the map command
/// to the command objects, executes them, and builds the result assembly
/// components needed to return the data reader
///
///
///
///
/// behavior must specify CommandBehavior.SequentialAccess
/// input parameters in the entityCommand.Parameters collection must have non-null values.
internal DbDataReader Execute(EntityCommand entityCommand, CommandBehavior behavior) {
IntPtr hscp;
EntityBid.ScopeEnter(out hscp, " %d#\n", ObjectID);
try {
if (CommandBehavior.SequentialAccess != (behavior & CommandBehavior.SequentialAccess)) {
throw EntityUtil.MustUseSequentialAccess();
}
DbDataReader storeDataReader = ExecuteStoreCommands(entityCommand, behavior);
DbDataReader result = null;
// If we actually executed something, then go ahead and construct a bridge
// data reader for it.
if (null != storeDataReader) {
try {
ColumnMap columnMap = _columnMapGenerator.CreateColumnMap(storeDataReader);
if (null == columnMap) {
// For a query with no result type (and therefore no column map), consume the reader.
// When the user requests Metadata for this reader, we return nothing.
CommandHelper.ConsumeReader(storeDataReader);
result = storeDataReader;
}
else {
result = BridgeDataReader.Create(storeDataReader, columnMap, entityCommand.Connection.GetMetadataWorkspace());
}
}
catch {
// dispose of store reader if there is an error creating the BridgeDataReader
storeDataReader.Dispose();
throw;
}
}
return result;
}
finally {
EntityBid.ScopeLeave(ref hscp);
}
}
///
/// Execute the store commands, and return IteratorSources for each one
///
///
///
internal DbDataReader ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) {
// SQLPT #120007433 is the work item to implement MARS support, which we
// need to do here, but since the PlanCompiler doesn't
// have it yet, neither do we...
if (1 != _mappedCommandDefinitions.Count) {
throw EntityUtil.NotSupported("MARS");
}
EntityTransaction entityTransaction = CommandHelper.GetEntityTransaction(entityCommand);
DbCommandDefinition definition = _mappedCommandDefinitions[0];
DbCommand storeProviderCommand = definition.CreateCommand();
CommandHelper.SetStoreProviderCommandState(entityCommand, entityTransaction, storeProviderCommand);
// Copy over the values from the map command to the store command; we
// assume that they were not renamed by either the plan compiler or SQL
// Generation.
//
// Note that this pretty much presumes that named parameters are supported
// by the store provider, but it might work if we don't reorder/reuse
// parameters.
//
// Note also that the store provider may choose to add parameters to thier
// command object for some things; we'll only copy over the values for
// parameters that we find in the EntityCommands parameters collection, so
// we won't damage anything the store provider did.
bool hasOutputParameters = false;
if (storeProviderCommand.Parameters != null) // SQLBUDT 519066
{
DbProviderServices storeProviderServices = DbProviderServices.GetProviderServices(entityCommand.Connection.StoreProviderFactory);
foreach (DbParameter storeParameter in storeProviderCommand.Parameters) {
// I could just use the string indexer, but then if I didn't find it the
// consumer would get some ParameterNotFound exeception message and that
// wouldn't be very meaningful. Instead, I use the IndexOf method and
// if I don't find it, it's not a big deal (The store provider must
// have added it).
int parameterOrdinal = entityCommand.Parameters.IndexOf(storeParameter.ParameterName);
if (-1 != parameterOrdinal) {
EntityParameter entityParameter = entityCommand.Parameters[parameterOrdinal];
SyncParameterProperties(entityParameter, storeParameter, storeProviderServices);
if (storeParameter.Direction != ParameterDirection.Input) {
hasOutputParameters = true;
}
}
}
}
// If the EntityCommand has output parameters, we must synchronize parameter values when
// the reader is closed. Tell the EntityCommand about the store command so that it knows
// where to pull those values from.
if (hasOutputParameters) {
entityCommand.SetStoreProviderCommand(storeProviderCommand);
}
DbDataReader reader = null;
try {
reader = storeProviderCommand.ExecuteReader(behavior & ~CommandBehavior.SequentialAccess);
}
catch (Exception e) {
// we should not be wrapping all exceptions
if (EntityUtil.IsCatchableExceptionType(e)) {
// we don't wan't folks to have to know all the various types of exceptions that can
// occur, so we just rethrow a CommandDefinitionException and make whatever we caught
// the inner exception of it.
throw EntityUtil.CommandExecution(System.Data.Entity.Strings.EntityClient_CommandDefinitionExecutionFailed, e);
}
throw;
}
return reader;
}
///
/// Updates storeParameter size, precision and scale properties from user provided parameter properties.
///
///
///
private static void SyncParameterProperties(EntityParameter entityParameter, DbParameter storeParameter, DbProviderServices storeProviderServices) {
IDbDataParameter dbDataParameter = (IDbDataParameter)storeParameter;
// DBType is not currently syncable; it's part of the cache key anyway; this is because we can't guarantee
// that the store provider will honor it -- (SqlClient doesn't...)
//if (entityParameter.IsDbTypeSpecified)
//{
// storeParameter.DbType = entityParameter.DbType;
//}
// Give the store provider the opportunity to set the value before any parameter state has been copied from
// the EntityParameter.
storeProviderServices.SetParameterValue(storeParameter, entityParameter.GetTypeUsage(), entityParameter.Value);
// Override the store provider parameter state with any explicitly specified values from the EntityParameter.
if (entityParameter.IsDirectionSpecified)
{
storeParameter.Direction = entityParameter.Direction;
}
if (entityParameter.IsIsNullableSpecified)
{
storeParameter.IsNullable = entityParameter.IsNullable;
}
if (entityParameter.IsSizeSpecified)
{
storeParameter.Size = entityParameter.Size;
}
if (entityParameter.IsPrecisionSpecified)
{
dbDataParameter.Precision = entityParameter.Precision;
}
if (entityParameter.IsScaleSpecified)
{
dbDataParameter.Scale = entityParameter.Scale;
}
}
///
/// Return the string used by EntityCommand and ObjectQuery ToTraceString"/>
///
///
internal string ToTraceString() {
if (_mappedCommandDefinitions != null) {
if (_mappedCommandDefinitions.Count == 1) {
// Gosh it sure would be nice if I could just get the inner commandText, but
// that would require more public surface area on DbCommandDefinition, or
// me to know about the inner object...
return _mappedCommandDefinitions[0].CreateCommand().CommandText;
}
else {
StringBuilder sb = new StringBuilder();
foreach (DbCommandDefinition commandDefinition in _mappedCommandDefinitions) {
DbCommand mappedCommand = commandDefinition.CreateCommand();
sb.Append(mappedCommand.CommandText);
}
return sb.ToString();
}
}
return string.Empty;
}
#endregion
#region nested types
///
/// Generates a column map given a data reader.
///
private interface IColumnMapGenerator {
///
/// Given a data reader, returns column map.
///
/// Data reader.
/// Column map.
ColumnMap CreateColumnMap(DbDataReader reader);
}
///
/// IColumnMapGenerator wrapping a constant instance of a column map (invariant with respect
/// to the given DbDataReader)
///
private sealed class ConstantColumnMapGenerator : IColumnMapGenerator {
private readonly ColumnMap _columnMap;
private readonly int _fieldsRequired;
internal ConstantColumnMapGenerator(ColumnMap columnMap, int fieldsRequired) {
_columnMap = columnMap;
_fieldsRequired = fieldsRequired;
}
ColumnMap IColumnMapGenerator.CreateColumnMap(DbDataReader reader) {
if (null != reader && reader.FieldCount < _fieldsRequired) {
throw EntityUtil.CommandExecution(System.Data.Entity.Strings.EntityClient_TooFewColumns);
}
return _columnMap;
}
}
///
/// Generates column maps for a function mapping.
///
private sealed class FunctionColumnMapGenerator : IColumnMapGenerator {
private readonly FunctionImportMapping _mapping;
private readonly EntitySet _entitySet;
private readonly StructuralType _baseStructuralType;
internal FunctionColumnMapGenerator(FunctionImportMapping mapping, EntitySet entitySet, StructuralType baseStructuralType)
{
_mapping = mapping;
_entitySet = entitySet;
_baseStructuralType = baseStructuralType;
}
ColumnMap IColumnMapGenerator.CreateColumnMap(DbDataReader reader)
{
return ColumnMapFactory.CreateFunctionImportStructuralTypeColumnMap(reader, _mapping, _entitySet, _baseStructuralType);
}
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ComponentEvent.cs
- VisualState.cs
- CodeAccessSecurityEngine.cs
- LicenseManager.cs
- XmlResolver.cs
- Freezable.cs
- QilGenerator.cs
- BamlRecordWriter.cs
- DataGridViewRowCollection.cs
- MessageHeaderAttribute.cs
- StringUtil.cs
- AuthenticationService.cs
- MenuItemStyleCollection.cs
- RegexRunner.cs
- TextDataBindingHandler.cs
- FormViewDeletedEventArgs.cs
- ListViewContainer.cs
- PrimitiveCodeDomSerializer.cs
- SoapEnumAttribute.cs
- ListBase.cs
- PatternMatcher.cs
- WCFServiceClientProxyGenerator.cs
- XmlWrappingWriter.cs
- OperandQuery.cs
- ACE.cs
- CharStorage.cs
- ToolStripItem.cs
- ZipPackage.cs
- SafeViewOfFileHandle.cs
- Encoder.cs
- IntellisenseTextBox.designer.cs
- ColumnCollection.cs
- Query.cs
- EventLogException.cs
- _FixedSizeReader.cs
- base64Transforms.cs
- SiblingIterators.cs
- CodeIdentifiers.cs
- EncodingInfo.cs
- JsonReader.cs
- BrowserCapabilitiesFactory.cs
- ControlCachePolicy.cs
- InternalDispatchObject.cs
- SqlNotificationRequest.cs
- BasicKeyConstraint.cs
- CatalogPartCollection.cs
- GeometryGroup.cs
- FieldBuilder.cs
- Type.cs
- Schema.cs
- Registration.cs
- webeventbuffer.cs
- DataObjectPastingEventArgs.cs
- ProcessInfo.cs
- TranslateTransform3D.cs
- DataGridViewSortCompareEventArgs.cs
- TimerElapsedEvenArgs.cs
- SafeEventLogWriteHandle.cs
- DataRowComparer.cs
- InstanceDataCollection.cs
- EventManager.cs
- Select.cs
- DeleteMemberBinder.cs
- RTLAwareMessageBox.cs
- autovalidator.cs
- PriorityItem.cs
- StringTraceRecord.cs
- PassportAuthenticationModule.cs
- TabControlEvent.cs
- ObjectKeyFrameCollection.cs
- HandleExceptionArgs.cs
- MetadataItemEmitter.cs
- TextUtf8RawTextWriter.cs
- FixedHyperLink.cs
- InProcStateClientManager.cs
- TextRangeEditLists.cs
- TrackBarRenderer.cs
- ClaimTypeRequirement.cs
- Span.cs
- SelectiveScrollingGrid.cs
- CodeExporter.cs
- AsyncResult.cs
- WinFormsComponentEditor.cs
- DataGridViewCellMouseEventArgs.cs
- EditBehavior.cs
- DomainUpDown.cs
- DesignTimeTemplateParser.cs
- IssuedTokenServiceCredential.cs
- AutoSizeComboBox.cs
- WebPartEditorOkVerb.cs
- MediaScriptCommandRoutedEventArgs.cs
- HighContrastHelper.cs
- TriggerAction.cs
- ContentElement.cs
- Point3DIndependentAnimationStorage.cs
- OpenTypeLayout.cs
- FloaterBaseParagraph.cs
- BaseParser.cs
- DrawToolTipEventArgs.cs
- SystemDropShadowChrome.cs