Esempio n. 1
0
            /// <summary>
            /// Performs join propagation.
            /// </summary>
            /// <returns>Changes propagated to the current join node in the update mapping view.</returns>
            internal ChangeNode Propagate()
            {
                // Construct an empty change node for the result
                ChangeNode result = Propagator.BuildChangeNode(m_joinExpression);

                // Gather all keys involved in the join
                JoinDictionary leftDeletes  = ProcessKeys(m_left.Deleted, m_leftKeySelectors);
                JoinDictionary leftInserts  = ProcessKeys(m_left.Inserted, m_leftKeySelectors);
                JoinDictionary rightDeletes = ProcessKeys(m_right.Deleted, m_rightKeySelectors);
                JoinDictionary rightInserts = ProcessKeys(m_right.Inserted, m_rightKeySelectors);
                var            allKeys      = leftDeletes.Keys
                                              .Concat(leftInserts.Keys)
                                              .Concat(rightDeletes.Keys)
                                              .Concat(rightInserts.Keys)
                                              .Distinct(m_parent.UpdateTranslator.KeyComparer);

                // Perform propagation one key at a time
                foreach (CompositeKey key in allKeys)
                {
                    Propagate(key, result, leftDeletes, leftInserts, rightDeletes, rightInserts);
                }

                // Construct a new placeholder (see ChangeNode.Placeholder) for the join result node.
                result.Placeholder = CreateResultTuple(Tuple.Create((CompositeKey)null, m_left.Placeholder), Tuple.Create((CompositeKey)null, m_right.Placeholder), result);

                return(result);
            }
Esempio n. 2
0
            /// <summary>
            /// Produces a hash table of all instances and processes join keys, adding them to the list
            /// of keys handled by this node.
            /// </summary>
            /// <param name="instances">List of instances (whether delete or insert) for this node.</param>
            /// <param name="keySelectors">Selectors for key components.</param>
            /// <returns>A map from join keys to instances.</returns>
            private JoinDictionary ProcessKeys(IEnumerable <PropagatorResult> instances, ReadOnlyCollection <DbExpression> keySelectors)
            {
                // Dictionary uses the composite key on both sides. This is because the composite key, in addition
                // to supporting comparison, maintains some context information (e.g., source of a value in the
                // state manager).
                var hash = new JoinDictionary(m_parent.UpdateTranslator.KeyComparer);

                foreach (PropagatorResult instance in instances)
                {
                    CompositeKey key = ExtractKey(instance, keySelectors, m_parent);
                    hash[key] = Tuple.Create(key, instance);
                }

                return(hash);
            }
Esempio n. 3
0
            /// <summary>
            /// Propagate all changes associated with a particular join key.
            /// </summary>
            /// <param name="key">Key.</param>
            /// <param name="result">Resulting changes are added to this result.</param>
            private void Propagate(CompositeKey key, ChangeNode result, JoinDictionary leftDeletes, JoinDictionary leftInserts,
                                   JoinDictionary rightDeletes, JoinDictionary rightInserts)
            {
                // Retrieve changes associates with this join key
                Tuple <CompositeKey, PropagatorResult> leftInsert  = null;
                Tuple <CompositeKey, PropagatorResult> leftDelete  = null;
                Tuple <CompositeKey, PropagatorResult> rightInsert = null;
                Tuple <CompositeKey, PropagatorResult> rightDelete = null;

                Ops input = Ops.Nothing;

                if (leftInserts.TryGetValue(key, out leftInsert))
                {
                    input |= Ops.LeftInsert;
                }
                if (leftDeletes.TryGetValue(key, out leftDelete))
                {
                    input |= Ops.LeftDelete;
                }
                if (rightInserts.TryGetValue(key, out rightInsert))
                {
                    input |= Ops.RightInsert;
                }
                if (rightDeletes.TryGetValue(key, out rightDelete))
                {
                    input |= Ops.RightDelete;
                }

                // Get propagation rules for the changes
                Ops insertRule = m_insertRules[input];
                Ops deleteRule = m_deleteRules[input];

                if (Ops.Unsupported == insertRule || Ops.Unsupported == deleteRule)
                {
                    // If no propagation rules are defined, it suggests an invalid workload (e.g.
                    // a required entity or relationship is missing). In general, such exceptions
                    // should be caught by the RelationshipConstraintValidator, but we defensively
                    // check for problems here regardless. For instance, a 0..1:1..1 self-assocation
                    // implied a stronger constraint that cannot be checked by RelationshipConstraintValidator.

                    // First gather state entries contributing to the problem
                    List <IEntityStateEntry> stateEntries = new List <IEntityStateEntry>();
                    Action <Tuple <CompositeKey, PropagatorResult> > addStateEntries = (r) =>
                    {
                        if (r != null)
                        {
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(r.Item2, this.m_parent.m_updateTranslator,
                                                                                       this.m_parent.m_table));
                        }
                    };
                    addStateEntries(leftInsert);
                    addStateEntries(leftDelete);
                    addStateEntries(rightInsert);
                    addStateEntries(rightDelete);

                    throw EntityUtil.Update(Strings.Update_InvalidChanges, null, stateEntries);
                }

                // Where needed, substitute null/unknown placeholders. In some of the join propagation
                // rules, we handle the case where a side of the join is 'unknown', or where one side
                // of a join is comprised of an record containing only nulls. For instance, we may update
                // only one extent appearing in a row of a table (unknown), or; we may insert only
                // the left hand side of a left outer join, in which case the right hand side is 'null'.
                if (0 != (Ops.LeftUnknown & insertRule))
                {
                    leftInsert = Tuple.Create(key, LeftPlaceholder(key, PopulateMode.Unknown));
                }
                if (0 != (Ops.LeftUnknown & deleteRule))
                {
                    leftDelete = Tuple.Create(key, LeftPlaceholder(key, PopulateMode.Unknown));
                }
                if (0 != (Ops.RightNullModified & insertRule))
                {
                    rightInsert = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullModified));
                }
                else if (0 != (Ops.RightNullPreserve & insertRule))
                {
                    rightInsert = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullPreserve));
                }
                else if (0 != (Ops.RightUnknown & insertRule))
                {
                    rightInsert = Tuple.Create(key, RightPlaceholder(key, PopulateMode.Unknown));
                }

                if (0 != (Ops.RightNullModified & deleteRule))
                {
                    rightDelete = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullModified));
                }
                else if (0 != (Ops.RightNullPreserve & deleteRule))
                {
                    rightDelete = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullPreserve));
                }
                else if (0 != (Ops.RightUnknown & deleteRule))
                {
                    rightDelete = Tuple.Create(key, RightPlaceholder(key, PopulateMode.Unknown));
                }

                // Populate elements in join output
                if (null != leftInsert && null != rightInsert)
                {
                    result.Inserted.Add(CreateResultTuple(leftInsert, rightInsert, result));
                }
                if (null != leftDelete && null != rightDelete)
                {
                    result.Deleted.Add(CreateResultTuple(leftDelete, rightDelete, result));
                }
            }
            /// <summary>
            /// Produces a hash table of all instances and processes join keys, adding them to the list
            /// of keys handled by this node.
            /// </summary>
            /// <param name="instances">List of instances (whether delete or insert) for this node.</param>
            /// <param name="keySelectors">Selectors for key components.</param>
            /// <returns>A map from join keys to instances.</returns>
            private JoinDictionary ProcessKeys(IEnumerable<PropagatorResult> instances, ReadOnlyCollection<DbExpression> keySelectors)
            {
                // Dictionary uses the composite key on both sides. This is because the composite key, in addition
                // to supporting comparison, maintains some context information (e.g., source of a value in the
                // state manager).
                var hash = new JoinDictionary(m_parent.UpdateTranslator.KeyComparer);

                foreach (PropagatorResult instance in instances)
                {
                    CompositeKey key = ExtractKey(instance, keySelectors, m_parent);
                    hash[key] = Tuple.Create(key, instance);
                }

                return hash;
            }
            /// <summary>
            /// Propagate all changes associated with a particular join key.
            /// </summary>
            /// <param name="key">Key.</param>
            /// <param name="result">Resulting changes are added to this result.</param>
            private void Propagate(CompositeKey key, ChangeNode result, JoinDictionary leftDeletes, JoinDictionary leftInserts,
                JoinDictionary rightDeletes, JoinDictionary rightInserts)
            {
                // Retrieve changes associates with this join key
                Tuple<CompositeKey, PropagatorResult> leftInsert = null;
                Tuple<CompositeKey, PropagatorResult> leftDelete = null;
                Tuple<CompositeKey, PropagatorResult> rightInsert = null;
                Tuple<CompositeKey, PropagatorResult> rightDelete = null;

                Ops input = Ops.Nothing;

                if (leftInserts.TryGetValue(key, out leftInsert)) { input |= Ops.LeftInsert; }
                if (leftDeletes.TryGetValue(key, out leftDelete)) { input |= Ops.LeftDelete; }
                if (rightInserts.TryGetValue(key, out rightInsert)) { input |= Ops.RightInsert; } 
                if (rightDeletes.TryGetValue(key, out rightDelete)) { input |= Ops.RightDelete; }

                // Get propagation rules for the changes
                Ops insertRule = m_insertRules[input];
                Ops deleteRule = m_deleteRules[input];

                if (Ops.Unsupported == insertRule || Ops.Unsupported == deleteRule)
                {
                    // If no propagation rules are defined, it suggests an invalid workload (e.g.
                    // a required entity or relationship is missing). In general, such exceptions
                    // should be caught by the RelationshipConstraintValidator, but we defensively
                    // check for problems here regardless. For instance, a 0..1:1..1 self-assocation
                    // implied a stronger constraint that cannot be checked by RelationshipConstraintValidator.

                    // First gather state entries contributing to the problem
                    List<IEntityStateEntry> stateEntries = new List<IEntityStateEntry>();
                    Action<Tuple<CompositeKey, PropagatorResult>> addStateEntries = (r) =>
                        {
                            if (r != null)
                            {
                                stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(r.Item2, this.m_parent.m_updateTranslator,
                                    this.m_parent.m_table));
                            }
                        };
                    addStateEntries(leftInsert);
                    addStateEntries(leftDelete);
                    addStateEntries(rightInsert);
                    addStateEntries(rightDelete);

                    throw EntityUtil.Update(Strings.Update_InvalidChanges, null, stateEntries);
                }

                // Where needed, substitute null/unknown placeholders. In some of the join propagation
                // rules, we handle the case where a side of the join is 'unknown', or where one side
                // of a join is comprised of an record containing only nulls. For instance, we may update
                // only one extent appearing in a row of a table (unknown), or; we may insert only
                // the left hand side of a left outer join, in which case the right hand side is 'null'.
                if (0 != (Ops.LeftUnknown & insertRule))
                {
                    leftInsert = Tuple.Create(key, LeftPlaceholder(key, PopulateMode.Unknown));
                }
                if (0 != (Ops.LeftUnknown & deleteRule))
                {
                    leftDelete = Tuple.Create(key, LeftPlaceholder(key, PopulateMode.Unknown));
                }
                if (0 != (Ops.RightNullModified & insertRule)) 
                {
                    rightInsert = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullModified));
                }
                else if (0 != (Ops.RightNullPreserve & insertRule)) 
                {
                    rightInsert = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullPreserve));
                }
                else if (0 != (Ops.RightUnknown & insertRule)) 
                { 
                    rightInsert = Tuple.Create(key, RightPlaceholder(key, PopulateMode.Unknown)); 
                }

                if (0 != (Ops.RightNullModified & deleteRule)) 
                {
                    rightDelete = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullModified));
                }
                else if (0 != (Ops.RightNullPreserve & deleteRule)) 
                {
                    rightDelete = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullPreserve));
                }
                else if (0 != (Ops.RightUnknown & deleteRule)) 
                {
                    rightDelete = Tuple.Create(key, RightPlaceholder(key, PopulateMode.Unknown));
                }

                // Populate elements in join output
                if (null != leftInsert && null != rightInsert)
                {
                    result.Inserted.Add(CreateResultTuple(leftInsert, rightInsert, result));
                }
                if (null != leftDelete && null != rightDelete)
                {
                    result.Deleted.Add(CreateResultTuple(leftDelete, rightDelete, result));
                }
            }