File: System\Data\Common\Utils\CommandHelper.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="CommandHelper.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.Common.Utils
{
    using System.Data.EntityClient;
    using System.Data.Metadata.Edm;
    using System.Data.Spatial;
    using System.Diagnostics;
 
    /// <summary>
    /// Contains utility methods for construction of DB commands through generic
    /// provider interfaces.
    /// </summary>
    internal static class CommandHelper
    {
        /// <summary>
        /// Consumes all rows and result sets from the reader. This allows client to retrieve
        /// parameter values and intercept any store exceptions.
        /// </summary>
        /// <param name="reader">reader to consume</param>
        internal static void ConsumeReader(DbDataReader reader)
        {
            if (null != reader && !reader.IsClosed)
            {
                while (reader.NextResult())
                {
                    // Note that we only walk through the result sets. We don't need
                    // to walk through individual rows (though underlying provider
                    // implementation may do so)
                }
            }
        }
 
        /// <summary>
        /// requires: commandText must not be null
        /// The command text must be in the form Container.FunctionImportName.
        /// </summary>
        internal static void ParseFunctionImportCommandText(string commandText, string defaultContainerName, out string containerName, out string functionImportName)
        {
            Debug.Assert(null != commandText);
 
            // Split the string
            string[] nameParts = commandText.Split('.');
            containerName = null;
            functionImportName = null;
            if (2 == nameParts.Length)
            {
                containerName = nameParts[0].Trim();
                functionImportName = nameParts[1].Trim();
            }
            else if (1 == nameParts.Length && null != defaultContainerName)
            {
                containerName = defaultContainerName;
                functionImportName = nameParts[0].Trim();
            }
            if (string.IsNullOrEmpty(containerName) || string.IsNullOrEmpty(functionImportName))
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_InvalidStoredProcedureCommandText);
            }
        }
 
        /// <summary>
        /// Given an entity command, returns the associated entity transaction and performs validation
        /// to ensure the transaction is consistent.
        /// </summary>
        /// <param name="entityCommand">Entity command instance. Must not be null.</param>
        /// <returns>Entity transaction</returns>
        internal static EntityTransaction GetEntityTransaction(EntityCommand entityCommand)
        {
            Debug.Assert(null != entityCommand);
            EntityTransaction entityTransaction = (EntityTransaction)entityCommand.Transaction;
 
            // Check to make sure that either the command has no transaction associated with it, or it
            // matches the one used by the connection
            if (entityTransaction != null && entityTransaction != entityCommand.Connection.CurrentTransaction)
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_InvalidTransactionForCommand);
            }
            // Now we have asserted that EntityCommand either has no transaction or has one that matches the
            // one used in the connection, we can simply use the connection's transaction object
            entityTransaction = entityCommand.Connection.CurrentTransaction;
            return entityTransaction;
        }
 
 
        /// <summary>
        /// Given an entity command and entity transaction, passes through relevant state to store provider
        /// command.
        /// </summary>
        /// <param name="entityCommand">Entity command. Must not be null.</param>
        /// <param name="entityTransaction">Entity transaction. Must not be null.</param>
        /// <param name="storeProviderCommand">Store provider command that is being setup. Must not be null.</param>
        internal static void SetStoreProviderCommandState(EntityCommand entityCommand, EntityTransaction entityTransaction, DbCommand storeProviderCommand)
        {
            Debug.Assert(null != entityCommand);
            Debug.Assert(null != storeProviderCommand);
 
            storeProviderCommand.CommandTimeout = entityCommand.CommandTimeout;
            storeProviderCommand.Connection = ((EntityConnection)entityCommand.Connection).StoreConnection;
            storeProviderCommand.Transaction = (null != entityTransaction) ? entityTransaction.StoreTransaction : null;
            storeProviderCommand.UpdatedRowSource = entityCommand.UpdatedRowSource;
        }
 
 
        /// <summary>
        /// Given an entity command, store provider command and a connection, sets all output parameter values on the entity command.
        /// The connection is used to determine how to map spatial values.
        /// </summary>
        /// <param name="entityCommand">Entity command on which to set parameter values. Must not be null.</param>
        /// <param name="storeProviderCommand">Store provider command from which to retrieve parameter values. Must not
        /// be null.</param>
        /// <param name="connection">The connection on which the command was run.  Must not be null</param>
        internal static void SetEntityParameterValues(EntityCommand entityCommand, DbCommand storeProviderCommand, EntityConnection connection)
        {
            Debug.Assert(null != entityCommand);
            Debug.Assert(null != storeProviderCommand);
            Debug.Assert(null != connection);
 
            foreach (DbParameter storeParameter in storeProviderCommand.Parameters)
            {
                ParameterDirection direction = storeParameter.Direction;
                if (0 != (direction & ParameterDirection.Output))
                {
                    // if the entity command also defines the parameter, propagate store parameter value
                    // to entity parameter
                    int parameterOrdinal = entityCommand.Parameters.IndexOf(storeParameter.ParameterName);
                    if (0 <= parameterOrdinal)
                    {
                        EntityParameter entityParameter = entityCommand.Parameters[parameterOrdinal];
                        object parameterValue = storeParameter.Value;
                        TypeUsage parameterType = entityParameter.GetTypeUsage();
                        if (Helper.IsSpatialType(parameterType))
                        {
                            parameterValue = GetSpatialValueFromProviderValue(parameterValue, (PrimitiveType)parameterType.EdmType, connection);
                        }
                        entityParameter.Value = parameterValue;
                    }
                }
            }
        }
 
        private static object GetSpatialValueFromProviderValue(object spatialValue, PrimitiveType parameterType, EntityConnection connection)
        {
            DbProviderServices providerServices = DbProviderServices.GetProviderServices(connection.StoreConnection);
            StoreItemCollection storeItemCollection = (StoreItemCollection)connection.GetMetadataWorkspace().GetItemCollection(DataSpace.SSpace);
            DbSpatialServices spatialServices = providerServices.GetSpatialServices(storeItemCollection.StoreProviderManifestToken);
            if (Helper.IsGeographicType(parameterType))
            {
                return spatialServices.GeographyFromProviderValue(spatialValue);
            }
            else
            {
                Debug.Assert(Helper.IsGeometricType(parameterType));
                return spatialServices.GeometryFromProviderValue(spatialValue);
            }
        }
 
        // requires: all arguments must be given
        internal static EdmFunction FindFunctionImport(MetadataWorkspace workspace, string containerName, string functionImportName)
        {
            Debug.Assert(null != workspace && null != containerName && null != functionImportName);
            // find entity container
            EntityContainer entityContainer;
            if (!workspace.TryGetEntityContainer(containerName, DataSpace.CSpace, out entityContainer))
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnableToFindFunctionImportContainer(
                    containerName));
            }
 
            // find function import
            EdmFunction functionImport = null;
            foreach (EdmFunction candidate in entityContainer.FunctionImports)
            {
                if (candidate.Name == functionImportName)
                {
                    functionImport = candidate;
                    break;
                }
            }
            if (null == functionImport)
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnableToFindFunctionImport(
                    containerName, functionImportName));
            }
            if (functionImport.IsComposableAttribute)
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_FunctionImportMustBeNonComposable(containerName + "." + functionImportName));
            }
            return functionImport;
        }
    }
}