EntityCommandDefinition.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / EntityClient / EntityCommandDefinition.cs / 1 / EntityCommandDefinition.cs

                            //------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//----------------------------------------------------------------------------- 
 
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); 

                    // Construct store command tree usage. 
                    DbFunctionCommandTree providerCommandTree = new DbFunctionCommandTree(entityCommandTree.MetadataWorkspace, DataSpace.SSpace,
                        mapping.TargetFunction, storeResultType);

                    // Copy over parameters (this happens through a more indirect route in the plan compiler, but 
                    // it happens nonetheless)
                    foreach (KeyValuePair parameter in entityCommandTree.Parameters) { 
                        providerCommandTree.AddParameter(parameter.Key, parameter.Value); 
                    }
 
                    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 (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 three varieties: 
            // 1. Collection(Entity)
            // 2. Collection(PrimitiveType)
            // 3. No result type
            TypeUsage storeResultType; 
            {
                EntityType baseEntityType; 
                EdmFunction functionImport = mapping.FunctionImport; 

                // 1. Collection(Entity) 
                if (MetadataHelper.TryGetFunctionImportReturnEntityType(functionImport, out baseEntityType)) {
                    EntitySet entitySet = functionImport.EntitySet;
                    Debug.Assert(null != entitySet, "FunctionImport requires entity set if the return type is EntityType");
                    columnMapGenerator = new FunctionColumnMapGenerator(mapping, entitySet, baseEntityType); 

                    // 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); 
                }

                // 2. 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, null); 
                    columnMapGenerator = new ConstantColumnMapGenerator(collectionColumnMap, 1); 
                }
 
                // 3. No result type
                else {
                    storeResultType = null;
                    columnMapGenerator = new ConstantColumnMapGenerator(null, 0); 
                }
            } 
            return storeResultType; 
        }
 
        /// 
        /// 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, _columnMapGenerator.CreateColumnMap(storeDataReader), 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 
            {
                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); 

                        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) { 
            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; 
            //}
            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; 
            }
            storeParameter.Value = entityParameter.Value;
        }
 
        /// 
        /// 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 EntityType _baseEntityType;
 
            internal FunctionColumnMapGenerator(FunctionImportMapping mapping, EntitySet entitySet, EntityType baseEntityType) {
                _mapping = mapping;
                _entitySet = entitySet;
                _baseEntityType = baseEntityType; 
            }
 
            ColumnMap IColumnMapGenerator.CreateColumnMap(DbDataReader reader) { 
                return ColumnMapFactory.CreateFunctionImportEntityColumnMap(reader, _mapping, _entitySet, _baseEntityType);
            } 
        }
        #endregion
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//----------------------------------------------------------------------------- 
 
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); 

                    // Construct store command tree usage. 
                    DbFunctionCommandTree providerCommandTree = new DbFunctionCommandTree(entityCommandTree.MetadataWorkspace, DataSpace.SSpace,
                        mapping.TargetFunction, storeResultType);

                    // Copy over parameters (this happens through a more indirect route in the plan compiler, but 
                    // it happens nonetheless)
                    foreach (KeyValuePair parameter in entityCommandTree.Parameters) { 
                        providerCommandTree.AddParameter(parameter.Key, parameter.Value); 
                    }
 
                    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 (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 three varieties: 
            // 1. Collection(Entity)
            // 2. Collection(PrimitiveType)
            // 3. No result type
            TypeUsage storeResultType; 
            {
                EntityType baseEntityType; 
                EdmFunction functionImport = mapping.FunctionImport; 

                // 1. Collection(Entity) 
                if (MetadataHelper.TryGetFunctionImportReturnEntityType(functionImport, out baseEntityType)) {
                    EntitySet entitySet = functionImport.EntitySet;
                    Debug.Assert(null != entitySet, "FunctionImport requires entity set if the return type is EntityType");
                    columnMapGenerator = new FunctionColumnMapGenerator(mapping, entitySet, baseEntityType); 

                    // 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); 
                }

                // 2. 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, null); 
                    columnMapGenerator = new ConstantColumnMapGenerator(collectionColumnMap, 1); 
                }
 
                // 3. No result type
                else {
                    storeResultType = null;
                    columnMapGenerator = new ConstantColumnMapGenerator(null, 0); 
                }
            } 
            return storeResultType; 
        }
 
        /// 
        /// 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, _columnMapGenerator.CreateColumnMap(storeDataReader), 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 
            {
                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); 

                        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) { 
            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; 
            //}
            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; 
            }
            storeParameter.Value = entityParameter.Value;
        }
 
        /// 
        /// 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 EntityType _baseEntityType;
 
            internal FunctionColumnMapGenerator(FunctionImportMapping mapping, EntitySet entitySet, EntityType baseEntityType) {
                _mapping = mapping;
                _entitySet = entitySet;
                _baseEntityType = baseEntityType; 
            }
 
            ColumnMap IColumnMapGenerator.CreateColumnMap(DbDataReader reader) { 
                return ColumnMapFactory.CreateFunctionImportEntityColumnMap(reader, _mapping, _entitySet, _baseEntityType);
            } 
        }
        #endregion
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK