FunctionUpdateCommand.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Map / Update / Internal / FunctionUpdateCommand.cs / 1305376 / FunctionUpdateCommand.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Data.Common.CommandTrees; 
using System.Data.Metadata.Edm;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Common.CommandTrees.Internal; 
using System.Data.Common.Utils;
using System.Diagnostics; 
using System.Data.Common; 
using System.Data.Objects;
using System.Data.EntityClient; 
using System.Globalization;
using System.Data.Entity;
using System.Linq;
namespace System.Data.Mapping.Update.Internal 
{
    ///  
    /// Aggregates information about a modification command delegated to a store function. 
    /// 
    internal sealed class FunctionUpdateCommand : UpdateCommand 
    {
        #region Constructors
        /// 
        /// Initialize a new function command. Initializes the command object. 
        /// 
        /// Function mapping metadata 
        /// Translator 
        /// State entries handled by this operation.
        /// 'Root' state entry being handled by this function. 
        internal FunctionUpdateCommand(StorageFunctionMapping functionMapping, UpdateTranslator translator,
            System.Collections.ObjectModel.ReadOnlyCollection stateEntries,
            ExtractedStateEntry stateEntry)
            : base(stateEntry.Original, stateEntry.Current) 
        {
            EntityUtil.CheckArgumentNull(functionMapping, "functionMapping"); 
            EntityUtil.CheckArgumentNull(translator, "translator"); 
            EntityUtil.CheckArgumentNull(stateEntries, "stateEntries");
 
            // populate the main state entry for error reporting
            m_stateEntries = stateEntries;

            // create a command 
            DbCommandDefinition commandDefinition = translator.GenerateCommandDefinition(functionMapping);
            m_dbCommand = commandDefinition.CreateCommand(); 
        } 
        #endregion
 
        #region Fields
        private readonly System.Collections.ObjectModel.ReadOnlyCollection m_stateEntries;

        ///  
        /// Gets the store command wrapped by this command.
        ///  
        private readonly DbCommand m_dbCommand; 

        ///  
        /// Gets pairs for column names and propagator results (so that we can associate reader results with
        /// the source records for server generated values).
        /// 
        private List> m_resultColumns; 

        ///  
        /// Gets map from identifiers (key component proxies) to parameters holding the actual 
        /// key values. Supports propagation of identifier values (fixup for server-gen keys)
        ///  
        private List> m_inputIdentifiers;

        /// 
        /// Gets map from identifiers (key component proxies) to column names producing the actual 
        /// key values. Supports propagation of identifier values (fixup for server-gen keys)
        ///  
        private Dictionary m_outputIdentifiers; 

        ///  
        /// Gets a reference to the rows affected output parameter for the stored procedure. May be null.
        /// 
        private DbParameter m_rowsAffectedParameter;
        #endregion 

        #region Properties 
        internal override IEnumerable InputIdentifiers 
        {
            get 
            {
                if (null == m_inputIdentifiers)
                {
                    yield break; 
                }
                else 
                { 
                    foreach (KeyValuePair inputIdentifier in m_inputIdentifiers)
                    { 
                        yield return inputIdentifier.Key;
                    }
                }
            } 
        }
 
        internal override IEnumerable OutputIdentifiers 
        {
            get 
            {
                if (null == m_outputIdentifiers)
                {
                    return Enumerable.Empty(); 
                }
                return m_outputIdentifiers.Keys; 
            } 
        }
 
        internal override UpdateCommandKind Kind
        {
            get { return UpdateCommandKind.Function; }
        } 
        #endregion
 
        #region Methods 
        /// 
        /// Gets state entries contributing to this function. Supports error reporting. 
        /// 
        internal override IList GetStateEntries(UpdateTranslator translator)
        {
            return m_stateEntries; 
        }
 
        // Adds and register a DbParameter to the current command. 
        internal void SetParameterValue(PropagatorResult result, StorageFunctionParameterBinding parameterBinding, UpdateTranslator translator)
        { 
            // retrieve DbParameter
            DbParameter parameter = this.m_dbCommand.Parameters[parameterBinding.Parameter.Name];
            parameter.Value = translator.KeyManager.GetPrincipalValue(result);
 
            // if the parameter corresponds to an identifier (key component), remember this fact in case
            // it's important for dependency ordering (e.g., output the identifier before creating it) 
            int identifier = result.Identifier; 
            if (PropagatorResult.NullIdentifier != identifier)
            { 
                const int initialSize = 2; // expect on average less than two input identifiers per command
                if (null == m_inputIdentifiers)
                {
                    m_inputIdentifiers = new List>(initialSize); 
                }
                foreach (int principal in translator.KeyManager.GetPrincipals(identifier)) 
                { 
                    m_inputIdentifiers.Add(new KeyValuePair(principal, parameter));
                } 
            }
        }

        // Adds and registers a DbParameter taking the number of rows affected 
        internal void RegisterRowsAffectedParameter(FunctionParameter rowsAffectedParameter)
        { 
            if (null != rowsAffectedParameter) 
            {
                Debug.Assert(rowsAffectedParameter.Mode == ParameterMode.Out || rowsAffectedParameter.Mode == ParameterMode.InOut, 
                    "when loading mapping metadata, we check that the parameter is an out parameter");
                m_rowsAffectedParameter = m_dbCommand.Parameters[rowsAffectedParameter.Name];
            }
        } 

        // Adds a result column binding from a column name (from the result set for the function) to 
        // a propagator result (which contains the context necessary to back-propagate the result). 
        // If the result is an identifier, binds the
        internal void AddResultColumn(UpdateTranslator translator, String columnName, PropagatorResult result) 
        {
            const int initializeSize = 2; // expect on average less than two result columns per command
            if (null == m_resultColumns)
            { 
                m_resultColumns = new List>(initializeSize);
            } 
            m_resultColumns.Add(new KeyValuePair(columnName, result)); 

            int identifier = result.Identifier; 
            if (PropagatorResult.NullIdentifier != identifier)
            {
                if (translator.KeyManager.HasPrincipals(identifier))
                { 
                    throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_GeneratedDependent(columnName));
                } 
 
                // register output identifier to enable fix-up and dependency tracking
                AddOutputIdentifier(columnName, identifier); 
            }
        }

        // Indicate that a column in the command result set (specified by 'columnName') produces the 
        // value for a key component (specified by 'identifier')
        private void AddOutputIdentifier(String columnName, int identifier) 
        { 
            const int initialSize = 2; // expect on average less than two identifier output per command
            if (null == m_outputIdentifiers) 
            {
                m_outputIdentifiers = new Dictionary(initialSize);
            }
            m_outputIdentifiers[identifier] = columnName; 
        }
 
        // efects: Executes the current function command in the given transaction and connection context. 
        // All server-generated values are added to the generatedValues list. If those values are identifiers, they are
        // also added to the identifierValues dictionary, which associates proxy identifiers for keys in the session 
        // with their actual values, permitting fix-up of identifiers across relationships.
        internal override long Execute(UpdateTranslator translator, EntityConnection connection, Dictionary identifierValues,
            List> generatedValues)
        { 
            // configure command to use the connection and transaction for this session
            m_dbCommand.Transaction = ((null != connection.CurrentTransaction) ? connection.CurrentTransaction.StoreTransaction : null); 
            m_dbCommand.Connection = connection.StoreConnection; 
            if (translator.CommandTimeout.HasValue)
            { 
                m_dbCommand.CommandTimeout = translator.CommandTimeout.Value;
            }

            // set all identifier inputs (to support propagation of identifier values across relationship 
            // boundaries)
            if (null != m_inputIdentifiers) 
            { 
                foreach (KeyValuePair inputIdentifier in m_inputIdentifiers)
                { 
                    object value;
                    if (identifierValues.TryGetValue(inputIdentifier.Key, out value))
                    {
                        // set the actual value for the identifier if it has been produced by some 
                        // other command
                        inputIdentifier.Value.Value = value; 
                    } 
                }
            } 

            // Execute the query
            long rowsAffected;
            if (null != m_resultColumns) 
            {
                // If there are result columns, read the server gen results 
                rowsAffected = 0; 
                using (DbDataReader reader = m_dbCommand.ExecuteReader(CommandBehavior.SequentialAccess))
                { 
                    // Retrieve only the first row from the first result set
                    if (reader.Read())
                    {
                        rowsAffected++; 

                        foreach (var resultColumn in m_resultColumns 
                            .Select(r => new KeyValuePair(GetColumnOrdinal(translator, reader, r.Key), r.Value)) 
                            .OrderBy(r => r.Key)) // order by column ordinal to avoid breaking SequentialAccess readers
                        { 
                            int columnOrdinal = resultColumn.Key;
                            object value = reader.GetValue(columnOrdinal);

                            // register for back-propagation 
                            PropagatorResult result = resultColumn.Value;
                            generatedValues.Add(new KeyValuePair(result, value)); 
 
                            // register identifier if it exists
                            int identifier = result.Identifier; 
                            if (PropagatorResult.NullIdentifier != identifier)
                            {
                                identifierValues.Add(identifier, value);
                            } 
                        }
                    } 
 
                    // Consume the current reader (and subsequent result sets) so that any errors
                    // executing the function can be intercepted 
                    CommandHelper.ConsumeReader(reader);
                }
            }
            else 
            {
                rowsAffected = m_dbCommand.ExecuteNonQuery(); 
            } 

            // if an explicit rows affected parameter exists, use this value instead 
            if (null != m_rowsAffectedParameter)
            {
                // by design, negative row counts indicate failure iff. an explicit rows
                // affected parameter is used 
                if (DBNull.Value.Equals(m_rowsAffectedParameter.Value))
                { 
                    rowsAffected = 0; 
                }
                else 
                {
                    try
                    {
                        rowsAffected = Convert.ToInt64(m_rowsAffectedParameter.Value, CultureInfo.InvariantCulture); 
                    }
                    catch (Exception e) 
                    { 
                        if (UpdateTranslator.RequiresContext(e))
                        { 
                            // wrap the exception
                            throw EntityUtil.Update(System.Data.Entity.Strings.Update_UnableToConvertRowsAffectedParameterToInt32(
                                m_rowsAffectedParameter.ParameterName, typeof(int).FullName), e, this.GetStateEntries(translator));
                        } 
                        throw;
                    } 
                } 
            }
 
            return rowsAffected;
        }

        private int GetColumnOrdinal(UpdateTranslator translator, DbDataReader reader, string columnName) 
        {
            int columnOrdinal; 
            try 
            {
                columnOrdinal = reader.GetOrdinal(columnName); 
            }
            catch (IndexOutOfRangeException)
            {
                throw EntityUtil.Update(System.Data.Entity.Strings.Update_MissingResultColumn(columnName), null, 
                    this.GetStateEntries(translator));
            } 
            return columnOrdinal; 
        }
 
        /// 
        /// Gets modification operator corresponding to the given entity state.
        /// 
        private static ModificationOperator GetModificationOperator(EntityState state) 
        {
            switch (state) 
            { 
                case EntityState.Modified:
                case EntityState.Unchanged: 
                    // unchanged entities correspond to updates (consider the case where
                    // the entity is not being modified but a collocated relationship is)
                    return ModificationOperator.Update;
 
                case EntityState.Added:
                    return ModificationOperator.Insert; 
 
                case EntityState.Deleted:
                    return ModificationOperator.Delete; 

                default:
                    Debug.Fail("unexpected entity state " + state);
                    return default(ModificationOperator); 
            }
        } 
 
        internal override int CompareToType(UpdateCommand otherCommand)
        { 
            Debug.Assert(!object.ReferenceEquals(this, otherCommand), "caller should ensure other command is different");

            FunctionUpdateCommand other = (FunctionUpdateCommand)otherCommand;
 
            // first state entry is the 'main' state entry for the command (see ctor)
            IEntityStateEntry thisParent = this.m_stateEntries[0]; 
            IEntityStateEntry otherParent = other.m_stateEntries[0]; 

            // order by operator 
            int result = (int)GetModificationOperator(thisParent.State) -
                (int)GetModificationOperator(otherParent.State);
            if (0 != result) { return result; }
 
            // order by entity set
            result = StringComparer.Ordinal.Compare(thisParent.EntitySet.Name, otherParent.EntitySet.Name); 
            if (0 != result) { return result; } 
            result = StringComparer.Ordinal.Compare(thisParent.EntitySet.EntityContainer.Name, otherParent.EntitySet.EntityContainer.Name);
            if (0 != result) { return result; } 

            // order by key values
            int thisInputIdentifierCount = (null == this.m_inputIdentifiers ? 0 : this.m_inputIdentifiers.Count);
            int otherInputIdentifierCount = (null == other.m_inputIdentifiers ? 0 : other.m_inputIdentifiers.Count); 
            result = thisInputIdentifierCount - otherInputIdentifierCount;
            if (0 != result) { return result; } 
            for (int i = 0; i < thisInputIdentifierCount; i++) 
            {
                DbParameter thisParameter = this.m_inputIdentifiers[i].Value; 
                DbParameter otherParameter = other.m_inputIdentifiers[i].Value;
                result = ByValueComparer.Default.Compare(thisParameter.Value, otherParameter.Value);
                if (0 != result) { return result; }
            } 

            // If the result is still zero, it means key values are all the same. Switch to synthetic identifiers 
            // to differentiate. 
            for (int i = 0; i < thisInputIdentifierCount; i++)
            { 
                int thisIdentifier = this.m_inputIdentifiers[i].Key;
                int otherIdentifier = other.m_inputIdentifiers[i].Key;
                result = thisIdentifier - otherIdentifier;
                if (0 != result) { return result; } 
            }
 
            return result; 
        }
 
        #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