/// <summary> /// Sets the expected column types for a given function command tree /// </summary> private void SetFunctionExpectedTypes(DbFunctionCommandTree tree, EFMySqlCommand cmd) { if (tree.ResultType != null) { Debug.Assert(tree.ResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType, Resources.WrongFunctionResultType); CollectionType collectionType = (CollectionType)(tree.ResultType.EdmType); EdmType elementType = collectionType.TypeUsage.EdmType; if (elementType.BuiltInTypeKind == BuiltInTypeKind.RowType) { ReadOnlyMetadataCollection<EdmMember> members = ((RowType)elementType).Members; cmd.ColumnTypes = new PrimitiveType[members.Count]; for (int ordinal = 0; ordinal < members.Count; ordinal++) { EdmMember member = members[ordinal]; PrimitiveType primitiveType = (PrimitiveType)member.TypeUsage.EdmType; cmd.ColumnTypes[ordinal] = primitiveType; } } else if (elementType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType) { cmd.ColumnTypes = new PrimitiveType[1]; cmd.ColumnTypes[0] = (PrimitiveType)elementType; } else { Debug.Fail(Resources.WrongFunctionResultType); } } }
/// <summary> /// don't let this be constructed publicly; /// </summary> /// <exception cref="EntityCommandCompilationException">Cannot prepare the command definition for execution; consult the InnerException for more information.</exception> /// <exception cref="NotSupportedException">The ADO.NET Data Provider you are using does not support CommandTrees.</exception> internal EntityCommandDefinition(DbProviderFactory storeProviderFactory, DbCommandTree commandTree) { EntityUtil.CheckArgumentNull(storeProviderFactory, "storeProviderFactory"); EntityUtil.CheckArgumentNull(commandTree, "commandTree"); DbProviderServices storeProviderServices = DbProviderServices.GetProviderServices(storeProviderFactory); try { if (DbCommandTreeKind.Query == commandTree.CommandTreeKind) { // Next compile the plan for the command tree List<ProviderCommandInfo> mappedCommandList = new List<ProviderCommandInfo>(); ColumnMap columnMap; int columnCount; PlanCompiler.Compile(commandTree, out mappedCommandList, out columnMap, out columnCount, out _entitySets); _columnMapGenerators = new IColumnMapGenerator[] {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<DbCommandDefinition>(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. FunctionImportMappingNonComposable mapping = GetTargetFunctionMapping(entityCommandTree); IList<FunctionParameter> returnParameters = entityCommandTree.EdmFunction.ReturnParameters; int resultSetCount = returnParameters.Count > 1 ? returnParameters.Count : 1; _columnMapGenerators = new IColumnMapGenerator[resultSetCount]; TypeUsage storeResultType = DetermineStoreResultType(entityCommandTree.MetadataWorkspace, mapping, 0, out _columnMapGenerators[0]); for (int i = 1; i < resultSetCount; i++) { DetermineStoreResultType(entityCommandTree.MetadataWorkspace, mapping, i, out _columnMapGenerators[i]); } // Copy over parameters (this happens through a more indirect route in the plan compiler, but // it happens nonetheless) List<KeyValuePair<string, TypeUsage>> providerParameters = new List<KeyValuePair<string, TypeUsage>>(); foreach (KeyValuePair<string, TypeUsage> 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<DbCommandDefinition>(1) { storeCommandDefinition }; EntitySet firstResultEntitySet = mapping.FunctionImport.EntitySets.FirstOrDefault(); if (firstResultEntitySet != null) { _entitySets = new Set<EntitySet>(); _entitySets.Add(mapping.FunctionImport.EntitySets.FirstOrDefault()); _entitySets.MakeReadOnly(); } } // Finally, build a list of the parameters that the resulting command should have; List<EntityParameter> parameterList = new List<EntityParameter>(); foreach (KeyValuePair<string, TypeUsage> queryParameter in commandTree.Parameters) { EntityParameter parameter = CreateEntityParameterFromQueryParameter(queryParameter); parameterList.Add(parameter); } _parameters = new System.Collections.ObjectModel.ReadOnlyCollection<EntityParameter>(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; } }
protected virtual void VisitFunctionCommandTree(DbFunctionCommandTree functionTree) { EntityUtil.CheckArgumentNull(functionTree, "functionTree"); }
/// <summary> /// Retrieves mapping for the given C-Space functionCommandTree /// </summary> private static FunctionImportMappingNonComposable GetTargetFunctionMapping(DbFunctionCommandTree functionCommandTree) { Debug.Assert(functionCommandTree.DataSpace == DataSpace.CSpace, "map from CSpace->SSpace function"); Debug.Assert(functionCommandTree != null, "null functionCommandTree"); Debug.Assert(!functionCommandTree.EdmFunction.IsComposableAttribute, "functionCommandTree.EdmFunction must be non-composable."); // Find mapped store function. FunctionImportMapping targetFunctionMapping; if (!functionCommandTree.MetadataWorkspace.TryGetFunctionImportMapping(functionCommandTree.EdmFunction, out targetFunctionMapping)) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnmappedFunctionImport(functionCommandTree.EdmFunction.FullName)); } return (FunctionImportMappingNonComposable)targetFunctionMapping; }
// Generates and caches a command definition for the given function internal DbCommandDefinition GenerateCommandDefinition(StorageModificationFunctionMapping functionMapping) { if (null == m_modificationFunctionCommandDefinitions) { m_modificationFunctionCommandDefinitions = new Dictionary<StorageModificationFunctionMapping,DbCommandDefinition>(); } DbCommandDefinition commandDefinition; if (!m_modificationFunctionCommandDefinitions.TryGetValue(functionMapping, out commandDefinition)) { // synthesize a RowType for this mapping TypeUsage resultType = null; if (null != functionMapping.ResultBindings && 0 < functionMapping.ResultBindings.Count) { List<EdmProperty> properties = new List<EdmProperty>(functionMapping.ResultBindings.Count); foreach (StorageModificationFunctionResultBinding resultBinding in functionMapping.ResultBindings) { properties.Add(new EdmProperty(resultBinding.ColumnName, resultBinding.Property.TypeUsage)); } RowType rowType = new RowType(properties); CollectionType collectionType = new CollectionType(rowType); resultType = TypeUsage.Create(collectionType); } // add function parameters IEnumerable<KeyValuePair<string, TypeUsage>> functionParams = functionMapping.Function.Parameters.Select(paramInfo => new KeyValuePair<string, TypeUsage>(paramInfo.Name, paramInfo.TypeUsage)); // construct DbFunctionCommandTree including implict return type DbFunctionCommandTree tree = new DbFunctionCommandTree(m_metadataWorkspace, DataSpace.SSpace, functionMapping.Function, resultType, functionParams); commandDefinition = m_providerServices.CreateCommandDefinition(tree); } return commandDefinition; }
/// <summary> /// Ensures we have the command tree, either the user passed us the tree, or an eSQL statement that we need to parse /// </summary> private void MakeCommandTree() { // We must have a connection before we come here Debug.Assert(_connection != null); // Do the work only if we don't have a command tree yet if (_preparedCommandTree == null) { DbCommandTree resultTree = null; if (_commandTreeSetByUser != null) { resultTree = _commandTreeSetByUser; } else if (CommandType.Text == CommandType) { if (!string.IsNullOrEmpty(_esqlCommandText)) { // The perspective to be used for the query compilation Perspective perspective = new ModelPerspective(_connection.GetMetadataWorkspace()); // get a dictionary of names and typeusage from entity parameter collection var queryParams = GetParameterTypeUsage(); resultTree = CqlQuery.Compile( _esqlCommandText, perspective, null /*parser option - use default*/, queryParams.Select(paramInfo => paramInfo.Value.Parameter(paramInfo.Key))).CommandTree; } else { // We have no command text, no command tree, so throw an exception if (_isCommandDefinitionBased) { // This command was based on a prepared command definition and has no command text, // so reprepare is not possible. To create a new command with different parameters // requires creating a new entity command definition and calling it's CreateCommand method. throw new InvalidOperationException(Strings.EntityClient_CannotReprepareCommandDefinitionBasedCommand); } else { throw new InvalidOperationException(Strings.EntityClient_NoCommandText); } } } else if (CommandType.StoredProcedure == CommandType) { // get a dictionary of names and typeusage from entity parameter collection IEnumerable<KeyValuePair<string, TypeUsage>> queryParams = GetParameterTypeUsage(); var function = DetermineFunctionImport(); resultTree = new DbFunctionCommandTree(Connection.GetMetadataWorkspace(), DataSpace.CSpace, function, null, queryParams); } // After everything is good and succeeded, assign the result to our field _preparedCommandTree = resultTree; } }
protected virtual void VisitFunctionCommandTree(DbFunctionCommandTree functionTree) { //Contract.Requires(functionTree != null); }