internal ExtractedStateEntry(EntityState state, PropagatorResult original, PropagatorResult current, IEntityStateEntry source)
 {
     State = state;
     Original = original;
     Current = current;
     Source = source;
 }
        internal DynamicUpdateCommand(TableChangeProcessor processor, UpdateTranslator translator, ModificationOperator op,
            PropagatorResult originalValues, PropagatorResult currentValues, DbModificationCommandTree tree,
            Dictionary<int, string> outputIdentifiers)
            : base(originalValues, currentValues)
        {
            m_processor = EntityUtil.CheckArgumentNull(processor, "processor");
            m_operator = op;
            m_modificationCommandTree = EntityUtil.CheckArgumentNull(tree, "commandTree");
            m_outputIdentifiers = outputIdentifiers; // may be null (not all commands have output identifiers)

            // initialize identifier information (supports lateral propagation of server gen values)
            if (ModificationOperator.Insert == op || ModificationOperator.Update == op)
            {
                const int capacity = 2; // "average" number of identifiers per row
                m_inputIdentifiers = new List<KeyValuePair<int ,DbSetClause>>(capacity);

                foreach (KeyValuePair<EdmMember, PropagatorResult> member in
                    Helper.PairEnumerations(TypeHelpers.GetAllStructuralMembers(this.CurrentValues.StructuralType),
                                             this.CurrentValues.GetMemberValues()))
                {
                    DbSetClause setter;
                    int identifier = member.Value.Identifier;

                    if (PropagatorResult.NullIdentifier != identifier &&
                        TryGetSetterExpression(tree, member.Key, op, out setter)) // can find corresponding setter
                    {
                        foreach (int principal in translator.KeyManager.GetPrincipals(identifier))
                        {
                            m_inputIdentifiers.Add(new KeyValuePair<int, DbSetClause>(principal, setter));
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Builds an update command.
        /// </summary>
        /// <param name="oldRow">Old value of the row being updated.</param>
        /// <param name="newRow">New value for the row being updated.</param>
        /// <param name="processor">Context for the table containing row.</param>
        /// <returns>Update command.</returns>
        internal UpdateCommand BuildUpdateCommand(
            PropagatorResult oldRow,
            PropagatorResult newRow, TableChangeProcessor processor)
        {
            // If we're updating a row, the row may not need to be touched (e.g., no concurrency validation required)
            var rowMustBeTouched = false;

            var target = GetTarget(processor);

            // Create set clauses and returning parameter
            Dictionary<int, string> outputIdentifiers;
            DbExpression returning;
            var setClauses = new List<DbModificationClause>();
            foreach (var clause in BuildSetClauses(
                target, newRow, oldRow, processor, /* insertMode */ false, out outputIdentifiers, out returning,
                ref rowMustBeTouched))
            {
                setClauses.Add(clause);
            }

            // Construct predicate identifying the row to modify
            var predicate = BuildPredicate(target, oldRow, newRow, processor, ref rowMustBeTouched);

            if (0 == setClauses.Count)
            {
                if (rowMustBeTouched)
                {
                    var stateEntries = new List<IEntityStateEntry>();
                    stateEntries.AddRange(
                        SourceInterpreter.GetAllStateEntries(
                            oldRow, m_translator, processor.Table));
                    stateEntries.AddRange(
                        SourceInterpreter.GetAllStateEntries(
                            newRow, m_translator, processor.Table));
                    if (stateEntries.All(it => (it.State == EntityState.Unchanged)))
                    {
                        rowMustBeTouched = false;
                    }
                }

                // Determine if there is nothing to do (i.e., no values to set, 
                // no computed columns, and no concurrency validation required)
                if (!rowMustBeTouched)
                {
                    return null;
                }
            }

            // Initialize DML command tree
            var commandTree =
                new DbUpdateCommandTree(
                    m_translator.MetadataWorkspace, DataSpace.SSpace, target, predicate, setClauses.AsReadOnly(), returning);

            // Create command
            UpdateCommand command = new DynamicUpdateCommand(
                processor, m_translator, ModificationOperator.Update, oldRow, newRow, commandTree, outputIdentifiers);

            return command;
        }
Example #4
0
        /// <summary>
        /// Finds all markup associated with the given source.
        /// </summary>
        /// <param name="source">Source expression. Must not be null.</param>
        /// <param name="translator">Translator containing session information.</param>
        /// <param name="sourceTable">Table from which the exception was thrown (must not be null).</param>
        /// <returns>Markup.</returns>
        internal static ReadOnlyCollection<IEntityStateEntry> GetAllStateEntries(PropagatorResult source, UpdateTranslator translator,
            EntitySet sourceTable)
        {
            Debug.Assert(null != source);
            Debug.Assert(null != translator);
            Debug.Assert(null != sourceTable);

            SourceInterpreter interpreter = new SourceInterpreter(translator, sourceTable);
            interpreter.RetrieveResultMarkup(source);

            return new ReadOnlyCollection<IEntityStateEntry>(interpreter.m_stateEntries);
        }
        private void RetrieveResultMarkup(PropagatorResult source)
        {
            Debug.Assert(null != source);

            if (source.Identifier
                != PropagatorResult.NullIdentifier)
            {
                // state entries travel with identifiers. several state entries may be merged
                // into a single identifier result via joins in the update mapping view
                do
                {
                    if (null != source.StateEntry)
                    {
                        m_stateEntries.Add(source.StateEntry);
                        if (source.Identifier
                            != PropagatorResult.NullIdentifier)
                        {
                            // if this is an identifier, it may also be registered with an "owner".
                            // Return the owner as well if the owner is also mapped to this table.
                            PropagatorResult owner;
                            if (m_translator.KeyManager.TryGetIdentifierOwner(source.Identifier, out owner) &&
                                null != owner.StateEntry
                                &&
                                ExtentInScope(owner.StateEntry.EntitySet))
                            {
                                m_stateEntries.Add(owner.StateEntry);
                            }

                            // Check if are any referential constraints. If so, the entity key
                            // implies that the dependent relationship instance is also being
                            // handled in this result.
                            foreach (var stateEntry in m_translator.KeyManager.GetDependentStateEntries(source.Identifier))
                            {
                                m_stateEntries.Add(stateEntry);
                            }
                        }
                    }
                    source = source.Next;
                }
                while (null != source);
            }
            else if (!source.IsSimple
                     && !source.IsNull)
            {
                // walk children
                foreach (var child in source.GetMemberValues())
                {
                    RetrieveResultMarkup(child);
                }
            }
        }
                /// <summary>
                /// Construct a new placeholder with the shape of the given placeholder. Key values are
                /// injected into the resulting place holder and default values are substituted with
                /// either propagator constants or progagator nulls depending on the mode established
                /// by the <paramref name="mode"/> flag.
                /// </summary>
                /// <remarks>
                /// The key is essentially an array of values. The key map indicates that for a particular
                /// placeholder an expression (keyMap.Keys) corresponds to some ordinal in the key array.
                /// </remarks>
                /// <param name="placeholder">Placeholder to clone</param>
                /// <param name="key">Key to substitute</param>
                /// <param name="placeholderKey">Key elements in the placeholder (ordinally aligned with 'key')</param>
                /// <param name="mode">Mode of operation.</param>
                /// <returns>Cloned placeholder with key values</returns>
                internal static PropagatorResult Populate(
                    PropagatorResult placeholder, CompositeKey key,
                    CompositeKey placeholderKey, PopulateMode mode)
                {
                    //Contract.Requires(placeholder != null);
                    //Contract.Requires(key != null);
                    //Contract.Requires(placeholderKey != null);

                    // Figure out which flags to apply to generated elements.
                    var isNull = mode == PopulateMode.NullModified || mode == PopulateMode.NullPreserve;
                    var preserve = mode == PopulateMode.NullPreserve || mode == PopulateMode.Unknown;
                    var flags = PropagatorFlags.NoFlags;
                    if (!isNull)
                    {
                        flags |= PropagatorFlags.Unknown;
                    } // only null values are known
                    if (preserve)
                    {
                        flags |= PropagatorFlags.Preserve;
                    }

                    var result = placeholder.Replace(
                        node =>
                            {
                                // See if this is a key element
                                var keyIndex = -1;
                                for (var i = 0; i < placeholderKey.KeyComponents.Length; i++)
                                {
                                    if (placeholderKey.KeyComponents[i] == node)
                                    {
                                        keyIndex = i;
                                        break;
                                    }
                                }

                                if (keyIndex != -1)
                                {
                                    // Key value.
                                    return key.KeyComponents[keyIndex];
                                }
                                else
                                {
                                    // for simple entries, just return using the markup context for this
                                    // populator
                                    var value = isNull ? null : node.GetSimpleValue();
                                    return PropagatorResult.CreateSimpleValue(flags, value);
                                }
                            });

                    return result;
                }
        /// <summary>
        /// Builds a delete command. 
        /// </summary>
        /// <param name="oldRow">Value of the row being deleted.</param>
        /// <param name="processor">Context for the table containing row.</param>
        /// <returns>Delete command.</returns>
        internal UpdateCommand BuildDeleteCommand(PropagatorResult oldRow, TableChangeProcessor processor)
        {
            // If we're deleting a row, the row must always be touched
            bool rowMustBeTouched = true;

            // Initialize DML command tree
            DbExpressionBinding target = GetTarget(processor);

            // Create delete predicate
            DbExpression predicate = BuildPredicate(target, oldRow, null, processor, ref rowMustBeTouched);
            DbDeleteCommandTree commandTree = new DbDeleteCommandTree(m_translator.MetadataWorkspace, DataSpace.SSpace, target, predicate);

            // Set command
            // Initialize delete command
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Delete, oldRow, null, commandTree, null);

            return command;
        }
                /// <summary>
                /// Construct a new placeholder with the shape of the given placeholder. Key values are
                /// injected into the resulting place holder and default values are substituted with
                /// either propagator constants or progagator nulls depending on the mode established
                /// by the <paramref name="mode"/> flag.
                /// </summary>
                /// <remarks>
                /// The key is essentially an array of values. The key map indicates that for a particular
                /// placeholder an expression (keyMap.Keys) corresponds to some ordinal in the key array.
                /// </remarks>
                /// <param name="placeholder">Placeholder to clone</param>
                /// <param name="key">Key to substitute</param>
                /// <param name="placeholderKey">Key elements in the placeholder (ordinally aligned with 'key')</param>
                /// <param name="mode">Mode of operation.</param>
                /// <param name="translator">Translator context.</param>
                /// <returns>Cloned placeholder with key values</returns>
                internal static PropagatorResult Populate(PropagatorResult placeholder, CompositeKey key, 
                    CompositeKey placeholderKey, PopulateMode mode, UpdateTranslator translator)
                {
                    EntityUtil.CheckArgumentNull(placeholder, "placeholder");
                    EntityUtil.CheckArgumentNull(key, "key");
                    EntityUtil.CheckArgumentNull(placeholderKey, "placeholderKey");
                    EntityUtil.CheckArgumentNull(translator, "translator");

                    // Figure out which flags to apply to generated elements.
                    bool isNull = mode == PopulateMode.NullModified || mode == PopulateMode.NullPreserve;
                    bool preserve = mode == PopulateMode.NullPreserve || mode == PopulateMode.Unknown;
                    PropagatorFlags flags = PropagatorFlags.NoFlags;
                    if (!isNull) { flags |= PropagatorFlags.Unknown; } // only null values are known
                    if (preserve) { flags |= PropagatorFlags.Preserve; }

                    PropagatorResult result = placeholder.Replace(node =>
                        {
                            // See if this is a key element
                            int keyIndex = -1;
                            for (int i = 0; i < placeholderKey.KeyComponents.Length; i++)
                            {
                                if (placeholderKey.KeyComponents[i] == node)
                                {
                                    keyIndex = i;
                                    break;
                                }
                            }

                            if (keyIndex != -1)
                            {
                                // Key value.
                                return key.KeyComponents[keyIndex];
                            }
                            else
                            {
                                // for simple entries, just return using the markup context for this
                                // populator
                                object value = isNull ? null : node.GetSimpleValue();
                                return PropagatorResult.CreateSimpleValue(flags, value);
                            }
                        });
                    
                    return result;
                }
        internal DynamicUpdateCommand(
            TableChangeProcessor processor, UpdateTranslator translator,
            ModificationOperator modificationOperator, PropagatorResult originalValues, PropagatorResult currentValues,
            DbModificationCommandTree tree, Dictionary<int, string> outputIdentifiers)
            : base(translator, originalValues, currentValues)
        {
            //Contract.Requires(processor != null);
            //Contract.Requires(translator != null);
            //Contract.Requires(tree != null);

            _processor = processor;
            _operator = modificationOperator;
            _modificationCommandTree = tree;
            _outputIdentifiers = outputIdentifiers; // may be null (not all commands have output identifiers)

            // initialize identifier information (supports lateral propagation of server gen values)
            if (ModificationOperator.Insert == modificationOperator
                || ModificationOperator.Update == modificationOperator)
            {
                const int capacity = 2; // "average" number of identifiers per row
                _inputIdentifiers = new List<KeyValuePair<int, DbSetClause>>(capacity);

                foreach (var member in
                    Helper.PairEnumerations(
                        TypeHelpers.GetAllStructuralMembers(CurrentValues.StructuralType),
                        CurrentValues.GetMemberValues()))
                {
                    DbSetClause setter;
                    var identifier = member.Value.Identifier;

                    if (PropagatorResult.NullIdentifier != identifier
                        &&
                        TryGetSetterExpression(tree, member.Key, modificationOperator, out setter)) // can find corresponding setter
                    {
                        foreach (var principal in translator.KeyManager.GetPrincipals(identifier))
                        {
                            _inputIdentifiers.Add(new KeyValuePair<int, DbSetClause>(principal, setter));
                        }
                    }
                }
            }
        }
 private void AddReferencedEntities(UpdateTranslator translator, PropagatorResult result, KeyToListMap <EntityKey, UpdateCommand> referencedEntities)
 {
     foreach (PropagatorResult property in result.GetMemberValues())
     {
         if (property.IsSimple && property.Identifier != PropagatorResult.NullIdentifier &&
             (PropagatorFlags.ForeignKey == (property.PropagatorFlags & PropagatorFlags.ForeignKey)))
         {
             foreach (int principal in translator.KeyManager.GetDirectReferences(property.Identifier))
             {
                 PropagatorResult owner;
                 if (translator.KeyManager.TryGetIdentifierOwner(principal, out owner) &&
                     null != owner.StateEntry)
                 {
                     Debug.Assert(!owner.StateEntry.IsRelationship, "owner must not be a relationship");
                     referencedEntities.Add(owner.StateEntry.EntityKey, this);
                 }
             }
         }
     }
 }
Example #11
0
        internal ExtractedStateEntry(UpdateTranslator translator, IEntityStateEntry stateEntry)
        {
            Debug.Assert(null != stateEntry, "stateEntry must not be null");
            this.State  = stateEntry.State;
            this.Source = stateEntry;

            switch (stateEntry.State)
            {
            case EntityState.Deleted:
                this.Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(
                    stateEntry, ModifiedPropertiesBehavior.AllModified);
                this.Current = null;
                break;

            case EntityState.Unchanged:
                this.Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(
                    stateEntry, ModifiedPropertiesBehavior.NoneModified);
                this.Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(
                    stateEntry, ModifiedPropertiesBehavior.NoneModified);
                break;

            case EntityState.Modified:
                this.Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(
                    stateEntry, ModifiedPropertiesBehavior.SomeModified);
                this.Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(
                    stateEntry, ModifiedPropertiesBehavior.SomeModified);
                break;

            case EntityState.Added:
                this.Original = null;
                this.Current  = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(
                    stateEntry, ModifiedPropertiesBehavior.AllModified);
                break;

            default:
                Debug.Fail("unexpected IEntityStateEntry.State for entity " + stateEntry.State);
                this.Original = null;
                this.Current  = null;
                break;
            }
        }
        internal ExtractedStateEntry(UpdateTranslator translator, IEntityStateEntry stateEntry)
        {
            //Contract.Requires(translator != null);
            //Contract.Requires(stateEntry != null);

            State = stateEntry.State;
            Source = stateEntry;

            switch (stateEntry.State)
            {
                case EntityState.Deleted:
                    Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.AllModified);
                    Current = null;
                    break;
                case EntityState.Unchanged:
                    Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.NoneModified);
                    Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.NoneModified);
                    break;
                case EntityState.Modified:
                    Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.SomeModified);
                    Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.SomeModified);
                    break;
                case EntityState.Added:
                    Original = null;
                    Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.AllModified);
                    break;
                default:
                    Contract.Assert(false, "Unexpected IEntityStateEntry.State for entity " + stateEntry.State);
                    Original = null;
                    Current = null;
                    break;
            }
        }
Example #13
0
            /// <summary>
            /// Produce a tuple containing joined rows.
            /// </summary>
            /// <param name="left">Left row.</param>
            /// <param name="right">Right row.</param>
            /// <param name="leftKey">Key used to join left element.</param>
            /// <param name="rightKey">Key used to join right element.</param>
            /// <param name="result">Result change node; used for type information.</param>
            /// <returns>Result of joining the input rows.</returns>
            private PropagatorResult CreateResultTuple(Tuple <CompositeKey, PropagatorResult> left, Tuple <CompositeKey, PropagatorResult> right, ChangeNode result)
            {
                // using ref compare to avoid triggering value based
                CompositeKey leftKey  = left.Item1;
                CompositeKey rightKey = right.Item1;
                Dictionary <PropagatorResult, PropagatorResult> map = null;

                if (!object.ReferenceEquals(null, leftKey) &&
                    !object.ReferenceEquals(null, rightKey) &&
                    !object.ReferenceEquals(leftKey, rightKey))
                {
                    // Merge key values from the left and the right (since they're equal, there's a possibility we'll
                    // project values only from the left or the right hand side and lose important context.)
                    CompositeKey mergedKey = leftKey.Merge(m_parent.m_updateTranslator.KeyManager, rightKey);
                    // create a dictionary so that we can replace key values with merged key values (carrying context
                    // from both sides)
                    map = new Dictionary <PropagatorResult, PropagatorResult>();
                    for (int i = 0; i < leftKey.KeyComponents.Length; i++)
                    {
                        map[leftKey.KeyComponents[i]]  = mergedKey.KeyComponents[i];
                        map[rightKey.KeyComponents[i]] = mergedKey.KeyComponents[i];
                    }
                }

                PropagatorResult[] joinRecordValues = new PropagatorResult[2];
                joinRecordValues[0] = left.Item2;
                joinRecordValues[1] = right.Item2;
                PropagatorResult join = PropagatorResult.CreateStructuralValue(joinRecordValues, (StructuralType)result.ElementType.EdmType, false);

                // replace with merged key values as appropriate
                if (null != map)
                {
                    PropagatorResult replacement;
                    join = join.Replace(original => map.TryGetValue(original, out replacement) ? replacement : original);
                }

                return(join);
            }
        internal ExtractedStateEntry(UpdateTranslator translator, IEntityStateEntry stateEntry)
        {
            Debug.Assert(null != stateEntry, "stateEntry must not be null");
            this.State = stateEntry.State;
            this.Source = stateEntry;

            switch (stateEntry.State)
            {
                case EntityState.Deleted:
                    this.Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.AllModified);
                    this.Current = null;
                    break;
                case EntityState.Unchanged:
                    this.Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.NoneModified);
                    this.Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.NoneModified);
                    break;
                case EntityState.Modified:
                    this.Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.SomeModified);
                    this.Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.SomeModified);
                    break;
                case EntityState.Added:
                    this.Original = null;
                    this.Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(
                        stateEntry, ModifiedPropertiesBehavior.AllModified);
                    break;
                default:
                    Debug.Fail("unexpected IEntityStateEntry.State for entity " + stateEntry.State);
                    this.Original = null;
                    this.Current = null;
                    break;
            }
        }
            internal override PropagatorResult Merge(KeyManager keyManager, PropagatorResult other)
            {
                KeyValue otherKey = other as KeyValue;

                if (null == otherKey)
                {
                    EntityUtil.InternalError(EntityUtil.InternalErrorCode.UpdatePipelineResultRequestInvalid, 0, "KeyValue.Merge");
                }

                // Determine which key (this or otherKey) is first in the chain. Principal keys take
                // precedence over dependent keys and entities take precedence over relationships.
                if (this.Identifier != otherKey.Identifier)
                {
                    // Find principal (if any)
                    if (keyManager.GetPrincipals(otherKey.Identifier).Contains(this.Identifier))
                    {
                        return(this.ReplicateResultWithNewNext(otherKey));
                    }
                    else
                    {
                        return(otherKey.ReplicateResultWithNewNext(this));
                    }
                }
                else
                {
                    // Entity takes precedence of relationship
                    if (null == m_stateEntry || m_stateEntry.IsRelationship)
                    {
                        return(otherKey.ReplicateResultWithNewNext(this));
                    }
                    else
                    {
                        return(this.ReplicateResultWithNewNext(otherKey));
                    }
                }
            }
 // extracts key values from row expression
 private static CompositeKey ExtractKey(PropagatorResult change, ReadOnlyCollection<DbExpression> keySelectors, Propagator parent)
 {
     Debug.Assert(null != change && null != keySelectors && null != parent);
     PropagatorResult[] keyValues = new PropagatorResult[keySelectors.Count];
     for (int i = 0; i < keySelectors.Count; i++)
     {
         PropagatorResult constant = Evaluator.Evaluate(keySelectors[i], change, parent);
         keyValues[i] = constant;
     }
     return new CompositeKey(keyValues);
 }
            // Find "sanctioned" default value
            private static void GetPropagatorResultForPrimitiveType(PrimitiveType primitiveType, out PropagatorResult result)
            {
                object value;
                PrimitiveTypeKind primitiveTypeKind = primitiveType.PrimitiveTypeKind;
                if (!s_typeDefaultMap.TryGetValue(primitiveTypeKind, out value))
                {
                    // If none exists, default to lowest common denominator for constants
                    value = default(byte);
                }

                // Return a new constant expression flagged as unknown since the value is only there for
                // show. (Not entirely for show, because null constraints may require a value for a record,
                // whether that record is a placeholder or not).
                result = PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value);
            }
            /// <summary>
            /// Specialization of <see cref="CreatePlaceholder" /> for a relationship set extent.
            /// </summary>
            /// <param name="associationSet"></param>
            /// <returns></returns>
            private PropagatorResult CreateAssociationSetPlaceholder(AssociationSet associationSet)
            {
                Debug.Assert(null != associationSet, "Caller must verify parameters are not null");

                var endMetadata = associationSet.ElementType.AssociationEndMembers;
                PropagatorResult[] endReferenceValues = new PropagatorResult[endMetadata.Count];

                // Create a reference expression for each end in the relationship
                for (int endOrdinal = 0; endOrdinal < endMetadata.Count; endOrdinal++)
                {
                    var end = endMetadata[endOrdinal];
                    EntityType entityType = (EntityType)((RefType)end.TypeUsage.EdmType).ElementType;

                    // Retrieve key values for this end
                    PropagatorResult[] keyValues = new PropagatorResult[entityType.KeyMembers.Count];
                    for (int memberOrdinal = 0; memberOrdinal < entityType.KeyMembers.Count; memberOrdinal++)
                    {
                        EdmMember keyMember = entityType.KeyMembers[memberOrdinal];
                        PropagatorResult keyValue = CreateMemberPlaceholder(keyMember);
                        keyValues[memberOrdinal] = keyValue;
                    }

                    RowType endType = entityType.GetKeyRowType(m_parent.MetadataWorkspace);
                    PropagatorResult refKeys = PropagatorResult.CreateStructuralValue(keyValues, endType, false);

                    endReferenceValues[endOrdinal] = refKeys;
                }

                PropagatorResult result = PropagatorResult.CreateStructuralValue(endReferenceValues, associationSet.ElementType, false);
                return result;
            }
Example #19
0
 /// <summary>
 /// Checks if the given identifier has a registered 'owner'
 /// </summary>
 internal bool TryGetIdentifierOwner(int identifier, out PropagatorResult owner)
 {
     owner = _identifiers[identifier].Owner;
     return(null != owner);
 }
Example #20
0
        /// <summary>
        /// Determines column/value used to set values for a row.
        /// </summary>
        /// <remarks>
        /// The following columns are not included in the result:
        /// <list>
        /// <item>Keys in non-insert operations (keys are only set for inserts).</item>
        /// <item>Values flagged 'preserve' (these are values the propagator claims are untouched).</item>
        /// <item>Server generated values.</item>
        /// </list>
        /// </remarks>
        /// <param name="target">Expression binding representing the table.</param>
        /// <param name="row">Row containing values to set.</param>
        /// <param name="processor">Context for table.</param>
        /// <param name="insertMode">Determines whether key columns and 'preserve' columns are
        /// omitted from the list.</param>
        /// <param name="outputIdentifiers">Dictionary listing server generated identifiers.</param>
        /// <param name="returning">DbExpression describing result projection for server generated values.</param>
        /// <param name="rowMustBeTouched">Indicates whether the row must be touched
        /// because it produces a value (e.g. computed)</param>
        /// <returns>Column value pairs.</returns>
        private IEnumerable <DbModificationClause> BuildSetClauses(DbExpressionBinding target, PropagatorResult row,
                                                                   PropagatorResult originalRow, TableChangeProcessor processor, bool insertMode, out Dictionary <int, string> outputIdentifiers, out DbExpression returning,
                                                                   ref bool rowMustBeTouched)
        {
            Dictionary <EdmProperty, PropagatorResult>  setClauses         = new Dictionary <EdmProperty, PropagatorResult>();
            List <KeyValuePair <string, DbExpression> > returningArguments = new List <KeyValuePair <string, DbExpression> >();

            outputIdentifiers = new Dictionary <int, string>();

            // Determine which flags indicate a property should be omitted from the set list.
            PropagatorFlags omitMask = insertMode ? PropagatorFlags.NoFlags :
                                       PropagatorFlags.Preserve | PropagatorFlags.Unknown;

            for (int propertyOrdinal = 0; propertyOrdinal < processor.Table.ElementType.Properties.Count; propertyOrdinal++)
            {
                EdmProperty property = processor.Table.ElementType.Properties[propertyOrdinal];

                // Type members and result values are ordinally aligned
                PropagatorResult propertyResult = row.GetMemberValue(propertyOrdinal);

                if (PropagatorResult.NullIdentifier != propertyResult.Identifier)
                {
                    // retrieve principal value
                    propertyResult = propertyResult.ReplicateResultWithNewValue(
                        m_translator.KeyManager.GetPrincipalValue(propertyResult));
                }

                bool omitFromSetList = false;

                Debug.Assert(propertyResult.IsSimple);

                // Determine if this is a key value
                bool isKey = false;
                for (int i = 0; i < processor.KeyOrdinals.Length; i++)
                {
                    if (processor.KeyOrdinals[i] == propertyOrdinal)
                    {
                        isKey = true;
                        break;
                    }
                }

                // check if this value should be omitted
                PropagatorFlags flags = PropagatorFlags.NoFlags;
                if (!insertMode && isKey)
                {
                    // Keys are only set for inserts
                    omitFromSetList = true;
                }
                else
                {
                    // See if this value has been marked up with some context. If so, add the flag information
                    // from the markup. Markup includes information about whether the property is a concurrency value,
                    // whether it is known (it may be a property that is preserved across an update for instance)
                    flags |= propertyResult.PropagatorFlags;
                }

                // Determine if this value is server-generated
                StoreGeneratedPattern genPattern = MetadataHelper.GetStoreGeneratedPattern(property);
                bool isServerGen = genPattern == StoreGeneratedPattern.Computed ||
                                   (insertMode && genPattern == StoreGeneratedPattern.Identity);
                if (isServerGen)
                {
                    DbPropertyExpression propertyExpression = target.Variable.Property(property);
                    returningArguments.Add(new KeyValuePair <string, DbExpression>(property.Name, propertyExpression));

                    // check if this is a server generated identifier
                    int identifier = propertyResult.Identifier;
                    if (PropagatorResult.NullIdentifier != identifier)
                    {
                        if (m_translator.KeyManager.HasPrincipals(identifier))
                        {
                            throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_GeneratedDependent(property.Name));
                        }
                        outputIdentifiers.Add(identifier, property.Name);

                        // If this property maps an identifier (in the update pipeline) it may
                        // also be a store key. If so, the pattern had better be "Identity"
                        // since otherwise we're dealing with a mutable key.
                        if (genPattern != StoreGeneratedPattern.Identity &&
                            processor.IsKeyProperty(propertyOrdinal))
                        {
                            throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_NotSupportedComputedKeyColumn(
                                                              EdmProviderManifest.StoreGeneratedPatternFacetName,
                                                              XmlConstants.Computed,
                                                              XmlConstants.Identity,
                                                              property.Name,
                                                              property.DeclaringType.FullName));
                        }
                    }
                }

                if (PropagatorFlags.NoFlags != (flags & (omitMask)))
                {
                    // column value matches "omit" pattern, therefore should not be set
                    omitFromSetList = true;
                }
                else if (isServerGen)
                {
                    // column value does not match "omit" pattern, but it is server generated
                    // so it cannot be set
                    omitFromSetList = true;

                    // if the row has a modified value overridden by server gen,
                    // it must still be touched in order to retrieve the value
                    rowMustBeTouched = true;
                }

                // make the user is not updating an identity value
                if (!omitFromSetList && !insertMode && genPattern == StoreGeneratedPattern.Identity)
                {
                    //throw the error only if the value actually changed
                    Debug.Assert(originalRow != null, "Updated records should have a original row");
                    PropagatorResult originalPropertyResult = originalRow.GetMemberValue(propertyOrdinal);
                    Debug.Assert(originalPropertyResult.IsSimple, "Server Gen property that is not primitive?");
                    Debug.Assert(propertyResult.IsSimple, "Server Gen property that is not primitive?");

                    if (!ByValueEqualityComparer.Default.Equals(originalPropertyResult.GetSimpleValue(), propertyResult.GetSimpleValue()))
                    {
                        throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_ModifyingIdentityColumn(
                                                              XmlConstants.Identity,
                                                              property.Name,
                                                              property.DeclaringType.FullName));
                    }
                    else
                    {
                        omitFromSetList = true;
                    }
                }

                if (!omitFromSetList)
                {
                    setClauses.Add(property, propertyResult);
                }
            }

            // Construct returning projection
            if (0 < returningArguments.Count)
            {
                returning = DbExpressionBuilder.NewRow(returningArguments);
            }
            else
            {
                returning = null;
            }

            // Construct clauses corresponding to the set clauses
            List <DbModificationClause> result = new List <DbModificationClause>(setClauses.Count);

            foreach (KeyValuePair <EdmProperty, PropagatorResult> setClause in setClauses)
            {
                EdmProperty property = setClause.Key;

                result.Add(new DbSetClause(
                               GeneratePropertyExpression(target, setClause.Key),
                               GenerateValueExpression(setClause.Key, setClause.Value)));
            }

            return(result);
        }
 // Effects: returns true iff. the input propagator result has some flag defined in "flags"
 // Requires: input is set
 private static bool HasFlag(PropagatorResult input, PropagatorFlags flags)
 {
     if (null == input) { return false; }
     return (PropagatorFlags.NoFlags != (flags & input.PropagatorFlags));
 }
        // Adds and register a DbParameter to the current command.
        internal void SetParameterValue(PropagatorResult result, StorageModificationFunctionParameterBinding parameterBinding, UpdateTranslator translator)
        {
            // retrieve DbParameter
            DbParameter parameter = this.m_dbCommand.Parameters[parameterBinding.Parameter.Name];
            TypeUsage parameterType = parameterBinding.Parameter.TypeUsage;
            object parameterValue = translator.KeyManager.GetPrincipalValue(result);
            translator.SetParameterValue(parameter, parameterType, parameterValue); 

            // 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<KeyValuePair<int, DbParameter>>(initialSize);
                }
                foreach (int principal in translator.KeyManager.GetPrincipals(identifier))
                {
                    m_inputIdentifiers.Add(new KeyValuePair<int, DbParameter>(principal, parameter));
                }
            }
        }
Example #23
0
        // Effects: given a "clause" in the form of a property/value pair, produces an equality expression. If the
        // value is null, creates an IsNull expression
        // Requires: all arguments are set
        private DbExpression GenerateEqualityExpression(DbExpressionBinding target, EdmProperty property, PropagatorResult value)
        {
            Debug.Assert(null != target && null != property && null != value);

            DbExpression propertyExpression = GeneratePropertyExpression(target, property);
            DbExpression valueExpression    = GenerateValueExpression(property, value);

            if (valueExpression.ExpressionKind == DbExpressionKind.Null)
            {
                return(propertyExpression.IsNull());
            }
            return(propertyExpression.Equal(valueExpression));
        }
        private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other)
        {
            KeyManager   keyManager = compiler.m_translator.KeyManager;
            CompositeKey otherKey   = new CompositeKey(GetKeyConstants(other));

            // determine if the conflict is due to shared principal key values
            bool sharedPrincipal = true;

            for (int i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++)
            {
                int identifier1 = key.KeyComponents[i].Identifier;
                int identifier2 = otherKey.KeyComponents[i].Identifier;

                if (!keyManager.GetPrincipals(identifier1).Intersect(keyManager.GetPrincipals(identifier2)).Any())
                {
                    sharedPrincipal = false;
                }
            }

            if (sharedPrincipal)
            {
                // if the duplication is due to shared principals, there is a duplicate key exception
                var stateEntries = SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, m_table)
                                   .Concat(SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, m_table));
                throw EntityUtil.Update(Strings.Update_DuplicateKeys, null, stateEntries);
            }
            else
            {
                // if there are no shared principals, it implies that common dependents are the problem
                HashSet <IEntityStateEntry> commonDependents = null;
                foreach (PropagatorResult keyValue in key.KeyComponents.Concat(otherKey.KeyComponents))
                {
                    var dependents = new HashSet <IEntityStateEntry>();
                    foreach (int dependentId in keyManager.GetDependents(keyValue.Identifier))
                    {
                        PropagatorResult dependentResult;
                        if (keyManager.TryGetIdentifierOwner(dependentId, out dependentResult) &&
                            null != dependentResult.StateEntry)
                        {
                            dependents.Add(dependentResult.StateEntry);
                        }
                    }
                    if (null == commonDependents)
                    {
                        commonDependents = new HashSet <IEntityStateEntry>(dependents);
                    }
                    else
                    {
                        commonDependents.IntersectWith(dependents);
                    }
                }

                // to ensure the exception shape is consistent with constraint violations discovered while processing
                // commands (a more conventional scenario in which different tables are contributing principal values)
                // wrap a DataConstraintException in an UpdateException
                throw EntityUtil.Update(Strings.Update_GeneralExecutionException,
                                        EntityUtil.Constraint(Strings.Update_ReferentialConstraintIntegrityViolation), commonDependents);
            }
        }
Example #25
0
            // Find "sanctioned" default value
            private static void GetPropagatorResultForPrimitiveType(PrimitiveType primitiveType, out PropagatorResult result)
            {
                object            value;
                PrimitiveTypeKind primitiveTypeKind = primitiveType.PrimitiveTypeKind;

                if (!s_typeDefaultMap.TryGetValue(primitiveTypeKind, out value))
                {
                    // If none exists, default to lowest common denominator for constants
                    value = default(byte);
                }

                // Return a new constant expression flagged as unknown since the value is only there for
                // show. (Not entirely for show, because null constraints may require a value for a record,
                // whether that record is a placeholder or not).
                result = PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value);
            }
        /// <summary>
        /// Converts a record to a propagator result
        /// </summary>
        /// <param name="stateEntry">state manager entry containing the record</param>
        /// <param name="isModified">Indicates whether the root element is modified (i.e., whether the type has changed)</param>
        /// <param name="record">Record to convert</param>
        /// <param name="useCurrentValues">Indicates whether we are retrieving current or original values.</param>
        /// <param name="translator">Translator for session context; registers new metadata for the record type if none
        /// exists</param>
        /// <param name="modifiedPropertiesBehavior">Indicates how to determine whether a property is modified.</param>
        /// <returns>Result corresponding to the given record</returns>
        internal static PropagatorResult ExtractResultFromRecord(IEntityStateEntry stateEntry, bool isModified, IExtendedDataRecord record, 
            bool useCurrentValues, UpdateTranslator translator, ModifiedPropertiesBehavior modifiedPropertiesBehavior)
        {
            StructuralType structuralType = (StructuralType)record.DataRecordInfo.RecordType.EdmType;
            ExtractorMetadata metadata = translator.GetExtractorMetadata(stateEntry.EntitySet, structuralType);
            EntityKey key = stateEntry.EntityKey;

            PropagatorResult[] nestedValues = new PropagatorResult[record.FieldCount];
            for (int ordinal = 0; ordinal < nestedValues.Length; ordinal++)
            {
                nestedValues[ordinal] = metadata.RetrieveMember(stateEntry, record, useCurrentValues, key,
                    ordinal, modifiedPropertiesBehavior);
            }

            return PropagatorResult.CreateStructuralValue(nestedValues, structuralType, isModified);
        }
        // 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 <KeyValuePair <string, PropagatorResult> >(initializeSize);
            }
            m_resultColumns.Add(new KeyValuePair <string, PropagatorResult>(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);
            }
        }
        // Extracts key constants from the given row.
        private PropagatorResult[] GetKeyConstants(PropagatorResult row)
        {
            PropagatorResult[] keyConstants = new PropagatorResult[m_keyOrdinals.Length];
            for (int i = 0; i < m_keyOrdinals.Length; i++)
            {
                PropagatorResult constant = row.GetMemberValue(m_keyOrdinals[i]);

                keyConstants[i] = constant;
            }
            return keyConstants;
        }
            internal override FunctionUpdateCommand Translate(
                UpdateTranslator translator,
                ExtractedStateEntry stateEntry)
            {
                var mapping = GetFunctionMapping(stateEntry);
                StorageEntityTypeModificationFunctionMapping typeMapping     = mapping.Item1;
                StorageModificationFunctionMapping           functionMapping = mapping.Item2;
                EntityKey entityKey = stateEntry.Source.EntityKey;

                var stateEntries = new HashSet <IEntityStateEntry> {
                    stateEntry.Source
                };

                // gather all referenced association ends
                var collocatedEntries =
                    // find all related entries corresponding to collocated association types
                    from end in functionMapping.CollocatedAssociationSetEnds
                    join candidateEntry in translator.GetRelationships(entityKey)
                    on end.CorrespondingAssociationEndMember.DeclaringType equals candidateEntry.EntitySet.ElementType
                    select Tuple.Create(end.CorrespondingAssociationEndMember, candidateEntry);

                var currentReferenceEnds  = new Dictionary <AssociationEndMember, IEntityStateEntry>();
                var originalReferenceEnds = new Dictionary <AssociationEndMember, IEntityStateEntry>();

                foreach (var candidate in collocatedEntries)
                {
                    ProcessReferenceCandidate(entityKey, stateEntries, currentReferenceEnds, originalReferenceEnds, candidate.Item1, candidate.Item2);
                }

                // create function object
                FunctionUpdateCommand command;

                // consider the following scenario, we need to loop through all the state entries that is correlated with entity2 and make sure it is not changed.
                // entity1 <-- Independent Association <-- entity2 <-- Fk association <-- entity 3
                //                                           |
                //              entity4 <-- Fk association <--
                if (stateEntries.All(e => e.State == EntityState.Unchanged))
                {
                    // we shouldn't update the entity if it is unchanged, only update when referenced association is changed.
                    // if not, then this will trigger a fake update for principal end as describe in
                    command = null;
                }
                else
                {
                    command = new FunctionUpdateCommand(functionMapping, translator, stateEntries.ToList().AsReadOnly(), stateEntry);

                    // bind all function parameters
                    BindFunctionParameters(translator, stateEntry, functionMapping, command, currentReferenceEnds, originalReferenceEnds);

                    // interpret all result bindings
                    if (null != functionMapping.ResultBindings)
                    {
                        foreach (StorageModificationFunctionResultBinding resultBinding in functionMapping.ResultBindings)
                        {
                            PropagatorResult result = stateEntry.Current.GetMemberValue(resultBinding.Property);
                            command.AddResultColumn(translator, resultBinding.ColumnName, result);
                        }
                    }
                }

                return(command);
            }
        // 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<KeyValuePair<string, PropagatorResult>>(initializeSize);
            }
            m_resultColumns.Add(new KeyValuePair<string, PropagatorResult>(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);
            }
        }
Example #31
0
        /// <summary>
        /// Determines predicate used to identify a row in a table.
        /// </summary>
        /// <remarks>
        /// Columns are included in the list when:
        /// <list>
        /// <item>They are keys for the table</item>
        /// <item>They are concurrency values</item>
        /// </list>
        /// </remarks>
        /// <param name="target">Expression binding representing the table containing the row</param>
        /// <param name="referenceRow">Values for the row being located.</param>
        /// <param name="current">Values being updated (may be null).</param>
        /// <param name="processor">Context for the table containing the row.</param>
        /// <param name="rowMustBeTouched">Output parameter indicating whether a row must be touched
        /// (whether it's being modified or not) because it contains a concurrency value</param>
        /// <returns>Column/value pairs.</returns>
        private DbExpression BuildPredicate(DbExpressionBinding target, PropagatorResult referenceRow, PropagatorResult current,
                                            TableChangeProcessor processor, ref bool rowMustBeTouched)
        {
            Dictionary <EdmProperty, PropagatorResult> whereClauses = new Dictionary <EdmProperty, PropagatorResult>();

            // add all concurrency tokens (note that keys are always concurrency tokens as well)
            int propertyOrdinal = 0;

            foreach (EdmProperty member in processor.Table.ElementType.Properties)
            {
                // members and result values are ordinally aligned
                PropagatorResult expectedValue = referenceRow.GetMemberValue(propertyOrdinal);
                PropagatorResult newValue      = null == current ? null : current.GetMemberValue(propertyOrdinal);

                // check if the rowMustBeTouched value should be set to true (if it isn't already
                // true and we've come across a concurrency value)
                if (!rowMustBeTouched &&
                    (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue) ||
                     HasFlag(newValue, PropagatorFlags.ConcurrencyValue)))
                {
                    rowMustBeTouched = true;
                }

                // determine if this is a concurrency value
                if (!whereClauses.ContainsKey(member) &&                                         // don't add to the set clause twice
                    (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key) ||
                     HasFlag(newValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key))) // tagged as concurrency value
                {
                    whereClauses.Add(member, expectedValue);
                }
                propertyOrdinal++;
            }

            // Build a binary AND expression tree from the clauses
            DbExpression predicate = null;

            foreach (KeyValuePair <EdmProperty, PropagatorResult> clause in whereClauses)
            {
                DbExpression clauseExpression = GenerateEqualityExpression(target, clause.Key, clause.Value);
                if (null == predicate)
                {
                    predicate = clauseExpression;
                }
                else
                {
                    predicate = predicate.And(clauseExpression);
                }
            }

            Debug.Assert(null != predicate, "some predicate term must exist");

            return(predicate);
        }
 /// <summary>
 /// A result is merged with another when it is merged as part of an equi-join.
 /// </summary>
 /// <remarks>
 /// In theory, this should only ever be called on two keys (since we only join on
 /// keys). We throw in the base implementation, and override in KeyResult. By convention
 /// the principal key is always the first result in the chain (in case of an RIC). In
 /// addition, entity entries always appear before relationship entries.
 /// </remarks>
 /// <param name="other">Result to merge with.</param>
 /// <returns>Merged result.</returns>
 internal virtual PropagatorResult Merge(KeyManager keyManager, PropagatorResult other)
 {
     throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UpdatePipelineResultRequestInvalid, 0, "PropagatorResult.Merge");
 }
 protected UpdateCommand(PropagatorResult originalValues, PropagatorResult currentValues)
 {
     m_originalValues = originalValues;
     m_currentValues  = currentValues;
 }
        /// <summary>
        /// Aligns a value returned from the store with the expected type for the member.
        /// </summary>
        /// <param name="value">Value to convert.</param>
        /// <param name="member">Metadata for the member being set.</param>
        /// <param name="context">The context generating the return value.</param>
        /// <returns>Converted return value</returns>
        private object AlignReturnValue(object value, EdmMember member, PropagatorResult context)
        {
            if (DBNull.Value.Equals(value))
            {
                // check if there is a nullability constraint on the value
                if (BuiltInTypeKind.EdmProperty == member.BuiltInTypeKind &&
                    !((EdmProperty)member).Nullable)
                {
                    throw EntityUtil.Update(System.Data.Entity.Strings.Update_NullReturnValueForNonNullableMember(
                        member.Name, 
                        member.DeclaringType.FullName), null);
                }
            }
            else if (!Helper.IsSpatialType(member.TypeUsage))
            {
                Type clrType;
                Type clrEnumType = null;
                if (Helper.IsEnumType(member.TypeUsage.EdmType))
                {
                    PrimitiveType underlyingType = Helper.AsPrimitive(member.TypeUsage.EdmType);
                    clrEnumType = context.Record.GetFieldType(context.RecordOrdinal);
                    clrType = underlyingType.ClrEquivalentType;
                    Debug.Assert(clrEnumType.IsEnum); 
                }
                else
                {
                    // convert the value to the appropriate CLR type
                    Debug.Assert(BuiltInTypeKind.PrimitiveType == member.TypeUsage.EdmType.BuiltInTypeKind,
                        "we only allow return values that are instances of EDM primitive or enum types");
                    PrimitiveType primitiveType = (PrimitiveType)member.TypeUsage.EdmType;
                    clrType = primitiveType.ClrEquivalentType;
                }

                try
                {
                    value = Convert.ChangeType(value, clrType, CultureInfo.InvariantCulture);
                    if (clrEnumType != null)
                    {
                        value = Enum.ToObject(clrEnumType, value);
                    }
                }
                catch (Exception e)
                {
                    // we should not be wrapping all exceptions
                    if (UpdateTranslator.RequiresContext(e)) 
                    {
                        Type userClrType = clrEnumType ?? clrType;
                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_ReturnValueHasUnexpectedType(
                            value.GetType().FullName,
                            userClrType.FullName,
                            member.Name,
                            member.DeclaringType.FullName), e);
                    }
                    throw;
                }
            }

            // return the adjusted value
            return value;
        }
Example #35
0
        internal override long Execute(UpdateTranslator translator, EntityConnection connection, Dictionary <int, object> identifierValues, List <KeyValuePair <PropagatorResult, object> > generatedValues)
        {
            // Compile command
            using (DbCommand command = this.CreateCommand(translator, identifierValues))
            {
                // configure command to use the connection and transaction for this session
                command.Transaction = ((null != connection.CurrentTransaction) ? connection.CurrentTransaction.StoreTransaction : null);
                command.Connection  = connection.StoreConnection;
                if (translator.CommandTimeout.HasValue)
                {
                    command.CommandTimeout = translator.CommandTimeout.Value;
                }

                // Execute the query
                int rowsAffected;
                if (m_modificationCommandTree.HasReader)
                {
                    // retrieve server gen results
                    rowsAffected = 0;
                    using (DbDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
                    {
                        if (reader.Read())
                        {
                            rowsAffected++;

                            IBaseList <EdmMember> members = TypeHelpers.GetAllStructuralMembers(this.CurrentValues.StructuralType);

                            for (int ordinal = 0; ordinal < reader.FieldCount; ordinal++)
                            {
                                // column name of result corresponds to column name of table
                                string    columnName = reader.GetName(ordinal);
                                EdmMember member     = members[columnName];
                                object    value;
                                if (Helper.IsSpatialType(member.TypeUsage) && !reader.IsDBNull(ordinal))
                                {
                                    value = SpatialHelpers.GetSpatialValue(translator.MetadataWorkspace, reader, member.TypeUsage, ordinal);
                                }
                                else
                                {
                                    value = reader.GetValue(ordinal);
                                }

                                // retrieve result which includes the context for back-propagation
                                int columnOrdinal       = members.IndexOf(member);
                                PropagatorResult result = this.CurrentValues.GetMemberValue(columnOrdinal);

                                // register for back-propagation
                                generatedValues.Add(new KeyValuePair <PropagatorResult, object>(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 command can be intercepted
                        CommandHelper.ConsumeReader(reader);
                    }
                }
                else
                {
                    rowsAffected = command.ExecuteNonQuery();
                }

                return(rowsAffected);
            }
        }
        /// <summary>
        /// Builds insert command.
        /// </summary>
        /// <param name="newRow">Row to insert.</param>
        /// <param name="processor">Context for the table we're inserting into.</param>
        /// <returns>Insert command.</returns>
        internal UpdateCommand BuildInsertCommand(PropagatorResult newRow, TableChangeProcessor processor)
        {
            // Bind the insert target
            DbExpressionBinding target = GetTarget(processor);

            // Create set clauses and returning parameter
            Dictionary<int, string> outputIdentifiers;
            DbExpression returning;
            bool rowMustBeTouched = true; // for inserts, the row must always be touched
            List<DbModificationClause> setClauses = new List<DbModificationClause>();
            foreach (DbModificationClause clause in BuildSetClauses(target, newRow, null, processor, /* insertMode */ true, out outputIdentifiers,
                out returning, ref rowMustBeTouched))
            {
                setClauses.Add(clause);
            }

            // Initialize DML command tree
            DbInsertCommandTree commandTree =
                new DbInsertCommandTree(m_translator.MetadataWorkspace, DataSpace.SSpace, target, setClauses.AsReadOnly(), returning);

            // Create command
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Insert, null, newRow, commandTree, outputIdentifiers);

            return command;
        }
            /// <summary>
            /// Specialization of <see cref="CreatePlaceholder" /> for an entity set extent.
            /// </summary>
            /// <param name="entitySet"></param>
            /// <returns></returns>
            private PropagatorResult CreateEntitySetPlaceholder(EntitySet entitySet)
            {
                EntityUtil.CheckArgumentNull(entitySet, "entitySet");
                ReadOnlyMetadataCollection<EdmProperty> members = entitySet.ElementType.Properties;
                PropagatorResult[] memberValues = new PropagatorResult[members.Count];

                for (int ordinal = 0; ordinal < members.Count; ordinal++)
                {
                    PropagatorResult memberValue = CreateMemberPlaceholder(members[ordinal]);
                    memberValues[ordinal] = memberValue;
                }

                PropagatorResult result = PropagatorResult.CreateStructuralValue(memberValues, entitySet.ElementType, false);

                return result;
            }
        /// <summary>
        /// Determines column/value used to set values for a row.
        /// </summary>
        /// <remarks>
        /// The following columns are not included in the result:
        /// <list>
        /// <item>Keys in non-insert operations (keys are only set for inserts).</item>
        /// <item>Values flagged 'preserve' (these are values the propagator claims are untouched).</item>
        /// <item>Server generated values.</item>
        /// </list>
        /// </remarks>
        /// <param name="target">Expression binding representing the table.</param>
        /// <param name="row">Row containing values to set.</param>
        /// <param name="processor">Context for table.</param>
        /// <param name="insertMode">Determines whether key columns and 'preserve' columns are 
        /// omitted from the list.</param>
        /// <param name="outputIdentifiers">Dictionary listing server generated identifiers.</param>
        /// <param name="returning">DbExpression describing result projection for server generated values.</param>
        /// <param name="rowMustBeTouched">Indicates whether the row must be touched 
        /// because it produces a value (e.g. computed)</param>
        /// <returns>Column value pairs.</returns>
        private IEnumerable<DbModificationClause> BuildSetClauses(DbExpressionBinding target, PropagatorResult row,
            PropagatorResult originalRow, TableChangeProcessor processor, bool insertMode, out Dictionary<int, string> outputIdentifiers, out DbExpression returning,
            ref bool rowMustBeTouched)
        {
            Dictionary<EdmProperty, PropagatorResult> setClauses = new Dictionary<EdmProperty, PropagatorResult>();
            List<KeyValuePair<string, DbExpression>> returningArguments = new List<KeyValuePair<string, DbExpression>>();
            outputIdentifiers = new Dictionary<int, string>();

            // Determine which flags indicate a property should be omitted from the set list.
            PropagatorFlags omitMask = insertMode ? PropagatorFlags.NoFlags :
                PropagatorFlags.Preserve | PropagatorFlags.Unknown;

            for (int propertyOrdinal = 0; propertyOrdinal < processor.Table.ElementType.Properties.Count; propertyOrdinal++)
            {
                EdmProperty property = processor.Table.ElementType.Properties[propertyOrdinal];

                // Type members and result values are ordinally aligned
                PropagatorResult propertyResult = row.GetMemberValue(propertyOrdinal);

                if (PropagatorResult.NullIdentifier != propertyResult.Identifier)
                {
                    // retrieve principal value
                    propertyResult = propertyResult.ReplicateResultWithNewValue(
                        m_translator.KeyManager.GetPrincipalValue(propertyResult));
                }

                bool omitFromSetList = false;

                Debug.Assert(propertyResult.IsSimple);

                // Determine if this is a key value
                bool isKey = false;
                for (int i = 0; i < processor.KeyOrdinals.Length; i++)
                {
                    if (processor.KeyOrdinals[i] == propertyOrdinal)
                    {
                        isKey = true;
                        break;
                    }
                }

                // check if this value should be omitted
                PropagatorFlags flags = PropagatorFlags.NoFlags;
                if (!insertMode && isKey)
                {
                    // Keys are only set for inserts
                    omitFromSetList = true;
                }
                else
                {
                    // See if this value has been marked up with some context. If so, add the flag information
                    // from the markup. Markup includes information about whether the property is a concurrency value,
                    // whether it is known (it may be a property that is preserved across an update for instance)
                    flags |= propertyResult.PropagatorFlags;
                }

                // Determine if this value is server-generated
                StoreGeneratedPattern genPattern = MetadataHelper.GetStoreGeneratedPattern(property);
                bool isServerGen = genPattern == StoreGeneratedPattern.Computed ||
                    (insertMode && genPattern == StoreGeneratedPattern.Identity);
                if (isServerGen)
                {
                    DbPropertyExpression propertyExpression = target.Variable.Property(property);
                    returningArguments.Add(new KeyValuePair<string, DbExpression>(property.Name, propertyExpression));

                    // check if this is a server generated identifier
                    int identifier = propertyResult.Identifier;
                    if (PropagatorResult.NullIdentifier != identifier)
                    {
                        if (m_translator.KeyManager.HasPrincipals(identifier))
                        {
                            throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_GeneratedDependent(property.Name));
                        }
                        outputIdentifiers.Add(identifier, property.Name);

                        // If this property maps an identifier (in the update pipeline) it may
                        // also be a store key. If so, the pattern had better be "Identity"
                        // since otherwise we're dealing with a mutable key.
                        if (genPattern != StoreGeneratedPattern.Identity &&
                            processor.IsKeyProperty(propertyOrdinal))
                        {
                            throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_NotSupportedComputedKeyColumn(
                                EdmProviderManifest.StoreGeneratedPatternFacetName,
                                XmlConstants.Computed,
                                XmlConstants.Identity,
                                property.Name,
                                property.DeclaringType.FullName));
                        }
                    }
                }

                if (PropagatorFlags.NoFlags != (flags & (omitMask)))
                {
                    // column value matches "omit" pattern, therefore should not be set
                    omitFromSetList = true;
                }
                else if (isServerGen)
                {
                    // column value does not match "omit" pattern, but it is server generated
                    // so it cannot be set
                    omitFromSetList = true;

                    // if the row has a modified value overridden by server gen,
                    // it must still be touched in order to retrieve the value
                    rowMustBeTouched = true;
                }

                // make the user is not updating an identity value
                if (!omitFromSetList && !insertMode && genPattern == StoreGeneratedPattern.Identity)
                {
                    //throw the error only if the value actually changed
                    Debug.Assert(originalRow != null, "Updated records should have a original row");
                    PropagatorResult originalPropertyResult = originalRow.GetMemberValue(propertyOrdinal);
                    Debug.Assert(originalPropertyResult.IsSimple, "Server Gen property that is not primitive?");
                    Debug.Assert(propertyResult.IsSimple, "Server Gen property that is not primitive?");

                    if (!ByValueEqualityComparer.Default.Equals(originalPropertyResult.GetSimpleValue(), propertyResult.GetSimpleValue()))
                    {
                        throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_ModifyingIdentityColumn(
                            XmlConstants.Identity,
                            property.Name,
                            property.DeclaringType.FullName));
                    }
                    else
                    {
                        omitFromSetList = true;
                    }
                }

                if (!omitFromSetList) { setClauses.Add(property, propertyResult); }
            }

            // Construct returning projection
            if (0 < returningArguments.Count)
            {
                returning = DbExpressionBuilder.NewRow(returningArguments);
            }
            else
            {
                returning = null;
            }

            // Construct clauses corresponding to the set clauses
            List<DbModificationClause> result = new List<DbModificationClause>(setClauses.Count);
            foreach (KeyValuePair<EdmProperty, PropagatorResult> setClause in setClauses)
            {
                EdmProperty property = setClause.Key;

                result.Add(new DbSetClause(
                    GeneratePropertyExpression(target, setClause.Key),
                    GenerateValueExpression(setClause.Key, setClause.Value)));
            }

            return result;
        }
            /// <summary>
            /// Given default values for children members, produces a new default expression for the requested (parent) member.
            /// </summary>
            /// <param name="node">Parent member</param>
            /// <returns>Default value for parent member</returns>
            internal PropagatorResult Visit(EdmMember node)
            {
                PropagatorResult result;
                TypeUsage nodeType = Helper.GetModelTypeUsage(node);

                if (Helper.IsScalarType(nodeType.EdmType))
                {
                    GetPropagatorResultForPrimitiveType(Helper.AsPrimitive(nodeType.EdmType), out result);
                }
                else
                {
                    // Construct a new 'complex type' (really any structural type) member.
                    StructuralType structuralType = (StructuralType)nodeType.EdmType;
                    IBaseList<EdmMember> members = TypeHelpers.GetAllStructuralMembers(structuralType);

                    PropagatorResult[] args = new PropagatorResult[members.Count];
                    for (int ordinal = 0; ordinal < members.Count; ordinal++)
                    //                    foreach (EdmMember member in members)
                    {
                        args[ordinal] = Visit(members[ordinal]);
                    }

                    result = PropagatorResult.CreateStructuralValue(args, structuralType, false);
                }

                return result;
            }
        /// <summary>
        /// Determines predicate used to identify a row in a table.
        /// </summary>
        /// <remarks>
        /// Columns are included in the list when:
        /// <list>
        /// <item>They are keys for the table</item>
        /// <item>They are concurrency values</item>
        /// </list>
        /// </remarks>
        /// <param name="target">Expression binding representing the table containing the row</param>
        /// <param name="referenceRow">Values for the row being located.</param>
        /// <param name="current">Values being updated (may be null).</param>
        /// <param name="processor">Context for the table containing the row.</param>
        /// <param name="rowMustBeTouched">Output parameter indicating whether a row must be touched
        /// (whether it's being modified or not) because it contains a concurrency value</param>
        /// <returns>Column/value pairs.</returns>
        private DbExpression BuildPredicate(DbExpressionBinding target, PropagatorResult referenceRow, PropagatorResult current,
            TableChangeProcessor processor, ref bool rowMustBeTouched)
        {
            Dictionary<EdmProperty, PropagatorResult> whereClauses = new Dictionary<EdmProperty, PropagatorResult>();

            // add all concurrency tokens (note that keys are always concurrency tokens as well)
            int propertyOrdinal = 0;
            foreach (EdmProperty member in processor.Table.ElementType.Properties)
            {
                // members and result values are ordinally aligned
                PropagatorResult expectedValue = referenceRow.GetMemberValue(propertyOrdinal);
                PropagatorResult newValue = null == current ? null : current.GetMemberValue(propertyOrdinal);

                // check if the rowMustBeTouched value should be set to true (if it isn't already
                // true and we've come across a concurrency value)
                if (!rowMustBeTouched &&
                    (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue) ||
                     HasFlag(newValue, PropagatorFlags.ConcurrencyValue)))
                {
                    rowMustBeTouched = true;
                }

                // determine if this is a concurrency value
                if (!whereClauses.ContainsKey(member) && // don't add to the set clause twice
                    (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key) ||
                     HasFlag(newValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key))) // tagged as concurrency value
                {
                    whereClauses.Add(member, expectedValue);
                }
                propertyOrdinal++;
            }

            // Build a binary AND expression tree from the clauses
            DbExpression predicate = null;
            foreach (KeyValuePair<EdmProperty, PropagatorResult> clause in whereClauses)
            {
                DbExpression clauseExpression = GenerateEqualityExpression(target, clause.Key, clause.Value);
                if (null == predicate) { predicate = clauseExpression; }
                else { predicate = predicate.And(clauseExpression); }
            }

            Debug.Assert(null != predicate, "some predicate term must exist");

            return predicate;
        }
            /// <summary>
            /// Produce a tuple containing joined rows.
            /// </summary>
            /// <param name="left">Left row.</param>
            /// <param name="right">Right row.</param>
            /// <param name="leftKey">Key used to join left element.</param>
            /// <param name="rightKey">Key used to join right element.</param>
            /// <param name="result">Result change node; used for type information.</param>
            /// <returns>Result of joining the input rows.</returns>
            private PropagatorResult CreateResultTuple(Tuple<CompositeKey, PropagatorResult> left, Tuple<CompositeKey, PropagatorResult> right, ChangeNode result)
            {
                // using ref compare to avoid triggering value based
                CompositeKey leftKey = left.Item1;
                CompositeKey rightKey = right.Item1;
                Dictionary<PropagatorResult, PropagatorResult> map = null;
                if (!object.ReferenceEquals(null, leftKey) &&
                    !object.ReferenceEquals(null, rightKey) &&
                    !object.ReferenceEquals(leftKey, rightKey))
                {
                    // Merge key values from the left and the right (since they're equal, there's a possibility we'll
                    // project values only from the left or the right hand side and lose important context.)
                    CompositeKey mergedKey = leftKey.Merge(m_parent.m_updateTranslator.KeyManager, rightKey);
                    // create a dictionary so that we can replace key values with merged key values (carrying context
                    // from both sides)
                    map = new Dictionary<PropagatorResult, PropagatorResult>();
                    for (int i = 0; i < leftKey.KeyComponents.Length; i++)
                    {
                        map[leftKey.KeyComponents[i]] = mergedKey.KeyComponents[i];
                        map[rightKey.KeyComponents[i]] = mergedKey.KeyComponents[i];
                    }
                }

                PropagatorResult[] joinRecordValues = new PropagatorResult[2];
                joinRecordValues[0] = left.Item2;
                joinRecordValues[1] = right.Item2;
                PropagatorResult join = PropagatorResult.CreateStructuralValue(joinRecordValues, (StructuralType)result.ElementType.EdmType, false);

                // replace with merged key values as appropriate
                if (null != map)
                {
                    PropagatorResult replacement;
                    join = join.Replace(original => map.TryGetValue(original, out replacement) ? replacement : original);
                }

                return join;
            }
        // Effects: given a "clause" in the form of a property/value pair, produces an equality expression. If the
        // value is null, creates an IsNull expression
        // Requires: all arguments are set
        private DbExpression GenerateEqualityExpression(DbExpressionBinding target, EdmProperty property, PropagatorResult value)
        {
            Debug.Assert(null != target && null != property && null != value);

            DbExpression propertyExpression = GeneratePropertyExpression(target, property);
            DbExpression valueExpression = GenerateValueExpression(property, value);
            if (valueExpression.ExpressionKind == DbExpressionKind.Null)
            {
                return propertyExpression.IsNull();
            }
            return propertyExpression.Equal(valueExpression);
        }
        private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other)
        {
            KeyManager keyManager = compiler.m_translator.KeyManager;
            CompositeKey otherKey = new CompositeKey(GetKeyConstants(other));

            // determine if the conflict is due to shared principal key values
            bool sharedPrincipal = true;
            for (int i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++)
            {
                int identifier1 = key.KeyComponents[i].Identifier;
                int identifier2 = otherKey.KeyComponents[i].Identifier;

                if (!keyManager.GetPrincipals(identifier1).Intersect(keyManager.GetPrincipals(identifier2)).Any())
                {
                    sharedPrincipal = false;
                }
            }

            if (sharedPrincipal)
            {
                // if the duplication is due to shared principals, there is a duplicate key exception
                var stateEntries = SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, m_table)
                    .Concat(SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, m_table));
                throw EntityUtil.Update(Strings.Update_DuplicateKeys, null, stateEntries);
            }
            else
            {
                // if there are no shared principals, it implies that common dependents are the problem
                HashSet<IEntityStateEntry> commonDependents = null;
                foreach (PropagatorResult keyValue in key.KeyComponents.Concat(otherKey.KeyComponents))
                {
                    var dependents = new HashSet<IEntityStateEntry>();
                    foreach (int dependentId in keyManager.GetDependents(keyValue.Identifier))
                    {
                        PropagatorResult dependentResult;
                        if (keyManager.TryGetIdentifierOwner(dependentId, out dependentResult) &&
                            null != dependentResult.StateEntry)
                        {
                            dependents.Add(dependentResult.StateEntry);
                        }
                    }
                    if (null == commonDependents)
                    {
                        commonDependents = new HashSet<IEntityStateEntry>(dependents);
                    }
                    else
                    {
                        commonDependents.IntersectWith(dependents);
                    }
                }

                // to ensure the exception shape is consistent with constraint violations discovered while processing
                // commands (a more conventional scenario in which different tables are contributing principal values)
                // wrap a DataConstraintException in an UpdateException
                throw EntityUtil.Update(Strings.Update_GeneralExecutionException,
                    EntityUtil.Constraint(Strings.Update_ReferentialConstraintIntegrityViolation), commonDependents);
            }
        }
        // Effects: given a propagator result, produces a constant expression describing that value.
        // Requires: all arguments are set, and the value must be simple (scalar)
        private DbExpression GenerateValueExpression(EdmProperty property, PropagatorResult value)
        {
            Debug.Assert(null != value && value.IsSimple && null != property);
            Debug.Assert(Helper.IsPrimitiveType(property.TypeUsage.EdmType), "Properties in SSpace should be primitive.");

            if (value.IsNull)
            {
                return DbExpressionBuilder.Null(Helper.GetModelTypeUsage(property));
            }
            object principalValue = m_translator.KeyManager.GetPrincipalValue(value);

            if (Convert.IsDBNull(principalValue))
            {
                // although the result may be marked non-null (because it is an identifier) it is possible
                // there is no corresponding real value for the property yet
                return DbExpressionBuilder.Null(Helper.GetModelTypeUsage(property));
            }
            else
            {
                // At this point we have already done any needed type checking and we potentially translated the type 
                // of the property to the SSpace (the property parameter is a property in the SSpace). However the value 
                // is here is a CSpace value. As a result it does not have to match the type of the property in SSpace.
                // Two cases here are:
                // - the type in CSpace does not exactly match the type in the SSpace (but is promotable)
                // - the type in CSpace is enum type and in this case it never matches the type in SSpace where enum type  
                //   does not exist
                // Since the types have already been checked it is safe just to convert the value from CSpace to the type
                // from SSpace.

                Debug.Assert(Nullable.GetUnderlyingType(principalValue.GetType()) == null, "Unexpected nullable type.");

                TypeUsage propertyType = Helper.GetModelTypeUsage(property);
                Type principalType = principalValue.GetType();

                if (principalType.IsEnum)
                {
                    principalValue = Convert.ChangeType(principalValue, principalType.GetEnumUnderlyingType(), CultureInfo.InvariantCulture);
                }

                var columnClrEquivalentType = ((PrimitiveType)propertyType.EdmType).ClrEquivalentType;

                if (principalType != columnClrEquivalentType)
                {
                    principalValue = Convert.ChangeType(principalValue, columnClrEquivalentType, CultureInfo.InvariantCulture);
                }

                return DbExpressionBuilder.Constant(propertyType, principalValue);
            }
        }
        private void SetServerGenValue(PropagatorResult context, object value)
        {
            if (context.RecordOrdinal != PropagatorResult.NullOrdinal)
            {
                CurrentValueRecord targetRecord = context.Record;

                // determine if type compensation is required
                IExtendedDataRecord recordWithMetadata = (IExtendedDataRecord)targetRecord;
                EdmMember member = recordWithMetadata.DataRecordInfo.FieldMetadata[context.RecordOrdinal].FieldType;

                value = value ?? DBNull.Value; // records expect DBNull rather than null
                value = AlignReturnValue(value, member, context);
                targetRecord.SetValue(context.RecordOrdinal, value);
            }
        }
        // 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 <int, object> identifierValues,
                                       List <KeyValuePair <PropagatorResult, object> > 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 <int, DbParameter> 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;
                IBaseList <EdmMember> members = TypeHelpers.GetAllStructuralMembers(this.CurrentValues.StructuralType);
                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 <int, PropagatorResult>(GetColumnOrdinal(translator, reader, r.Key), r.Value))
                                 .OrderBy(r => r.Key)) // order by column ordinal to avoid breaking SequentialAccess readers
                        {
                            int       columnOrdinal = resultColumn.Key;
                            TypeUsage columnType    = members[resultColumn.Value.RecordOrdinal].TypeUsage;
                            object    value;

                            if (Helper.IsSpatialType(columnType) && !reader.IsDBNull(columnOrdinal))
                            {
                                value = SpatialHelpers.GetSpatialValue(translator.MetadataWorkspace, reader, columnType, columnOrdinal);
                            }
                            else
                            {
                                value = reader.GetValue(columnOrdinal);
                            }

                            // register for back-propagation
                            PropagatorResult result = resultColumn.Value;
                            generatedValues.Add(new KeyValuePair <PropagatorResult, object>(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);
        }