Code:
/ 4.0 / 4.0 / 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 IEnumerableMappedCommands { 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 IEnumerableParameters { get { return _parameters; } } /// /// Set of entity sets exposed in the command. /// internal SetEntitySets { 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. //------------------------------------------------------------------------------ //// 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 IEnumerableMappedCommands { 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 IEnumerableParameters { get { return _parameters; } } /// /// Set of entity sets exposed in the command. /// internal SetEntitySets { 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
- PathParser.cs
- PropertyBuilder.cs
- Timer.cs
- ToolStripDropDownClosedEventArgs.cs
- WebConfigurationManager.cs
- ConnectionOrientedTransportBindingElement.cs
- Dynamic.cs
- ServiceProviders.cs
- XamlTreeBuilder.cs
- ModelUIElement3D.cs
- XamlToRtfWriter.cs
- TextDecoration.cs
- SoundPlayer.cs
- TreeViewImageIndexConverter.cs
- ReturnValue.cs
- FlowLayoutPanel.cs
- Latin1Encoding.cs
- DesignerDataColumn.cs
- WebBrowserSiteBase.cs
- ApplicationServiceManager.cs
- FigureParaClient.cs
- ReverseQueryOperator.cs
- BindingValueChangedEventArgs.cs
- OdbcPermission.cs
- UInt16.cs
- XmlCharType.cs
- HttpListenerRequestTraceRecord.cs
- X500Name.cs
- HTMLTextWriter.cs
- Resources.Designer.cs
- DataRelation.cs
- TdsParserSafeHandles.cs
- IInstanceTable.cs
- DataPagerFieldCommandEventArgs.cs
- CodeTypeMember.cs
- RootBrowserWindowProxy.cs
- SqlDataSourceCommandEventArgs.cs
- PointCollection.cs
- Operator.cs
- FieldBuilder.cs
- FlowDocumentPaginator.cs
- DataColumnMappingCollection.cs
- CapabilitiesAssignment.cs
- WebPartZoneBase.cs
- DynamicRendererThreadManager.cs
- BaseTemplateBuildProvider.cs
- XmlAtomErrorReader.cs
- TimerEventSubscriptionCollection.cs
- TextCompositionEventArgs.cs
- TypeNameParser.cs
- DataGridCaption.cs
- TreeNode.cs
- QilLiteral.cs
- DataGridPagerStyle.cs
- ServiceElement.cs
- SourceSwitch.cs
- StrongNamePublicKeyBlob.cs
- InlineObject.cs
- OptimalBreakSession.cs
- PointLightBase.cs
- UrlAuthorizationModule.cs
- XamlSerializer.cs
- ExpandableObjectConverter.cs
- XNameTypeConverter.cs
- UIPermission.cs
- GeneralTransform3DCollection.cs
- TreeViewImageIndexConverter.cs
- Int64Animation.cs
- SecurityProtocolCorrelationState.cs
- EntityProviderFactory.cs
- CacheRequest.cs
- LayoutTable.cs
- DataGridView.cs
- Currency.cs
- PropertyGridView.cs
- RuntimeVariableList.cs
- Assembly.cs
- ComponentDispatcher.cs
- ComponentResourceKey.cs
- TdsParserSessionPool.cs
- StringBuilder.cs
- DataGridViewCheckBoxColumn.cs
- CounterCreationDataConverter.cs
- WebScriptEndpoint.cs
- LoadRetryHandler.cs
- DataColumnChangeEvent.cs
- AttributeProviderAttribute.cs
- OpCodes.cs
- FastPropertyAccessor.cs
- DirtyTextRange.cs
- NoResizeHandleGlyph.cs
- ScrollPattern.cs
- BitmapEffectGroup.cs
- SrgsToken.cs
- XmlSchemaInclude.cs
- IISUnsafeMethods.cs
- StateRuntime.cs
- WmlControlAdapter.cs
- TrustLevelCollection.cs
- XmlDocumentFragment.cs