Propagator.JoinPropagator.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

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

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

using System.Data.Common.CommandTrees; 
using System.Collections.Generic;
using System.Data.Common.Utils;
using System.Diagnostics;
using System.Data.Metadata.Edm; 
using System.Data.Entity;
using System.Linq; 
using System.Collections.ObjectModel; 
namespace System.Data.Mapping.Update.Internal
{ 
    // We use CompositeKey on both sides of the dictionary because it is used both to identify rows that should be
    // joined (the Key part) and to carry context about the rows being joined (e.g. which components of the row
    // correspond to the join key).
    using JoinDictionary = Dictionary>; 
    internal partial class Propagator
    { 
        ///  
        /// Performs join propagation. The basic strategy is to identify changes (inserts, deletes)
        /// on either side of the join that are related according to the join criteria. Support is restricted 
        /// to conjunctions of equality predicates of the form left property == right property.
        /// When a group of related changes is identified, rules are applied based on the existence of
        /// different components (e.g., a left insert + right insert).
        ///  
        /// 
        /// The joins handled by this class are degenerate in the sense that a row in the 'left' input always 
        /// joins with at most one row in the 'right' input. The restrictions that allow for this assumption 
        /// are described in the update design spec (see 'Level 5 Optimization').
        ///  
        /// 
        /// Propagation rules for joins are stored in static fields of the class (initialized in the static
        /// constructor for the class).
        ///  
        private partial class JoinPropagator
        { 
            #region Constructors 
            /// 
            /// Constructs a join propagator. 
            /// 
            /// Result of propagating changes in the left input to the join
            /// Result of propagating changes in the right input to the join
            /// Join operator in update mapping view over which to propagate changes 
            /// Handler of propagation for the entire update mapping view
            internal JoinPropagator(ChangeNode left, ChangeNode right, DbJoinExpression node, Propagator parent) 
            { 
                EntityUtil.CheckArgumentNull(left, "left");
                EntityUtil.CheckArgumentNull(right, "right"); 
                EntityUtil.CheckArgumentNull(node, "node");
                EntityUtil.CheckArgumentNull(parent, "parent");

                m_left = left; 
                m_right = right;
                m_joinExpression = node; 
                m_parent = parent; 

                Debug.Assert(DbExpressionKind.LeftOuterJoin == node.ExpressionKind || DbExpressionKind.InnerJoin == node.ExpressionKind, "(Update/JoinPropagagtor/JoinEvaluator) " + 
                    "caller must ensure only left outer and inner joins are requested");
                // Retrieve propagation rules for the join type of the expression.
                if (DbExpressionKind.InnerJoin == m_joinExpression.ExpressionKind)
                { 
                    m_insertRules = s_innerJoinInsertRules;
                    m_deleteRules = s_innerJoinDeleteRules; 
                } 
                else
                { 
                    m_insertRules = s_leftOuterJoinInsertRules;
                    m_deleteRules = s_leftOuterJoinDeleteRules;
                }
 
                // Figure out key selectors involved in the equi-join (if it isn't an equi-join, we don't support it)
                JoinConditionVisitor.GetKeySelectors(node.JoinCondition, out m_leftKeySelectors, out m_rightKeySelectors); 
 
                // Find the key selector expressions in the left and right placeholders
                m_leftPlaceholderKey = ExtractKey(m_left.Placeholder, m_leftKeySelectors, m_parent); 
                m_rightPlaceholderKey = ExtractKey(m_right.Placeholder, m_rightKeySelectors, m_parent);
            }
            #endregion
 
            #region Fields
            #region Propagation rules 
            /** 
             * These static dictionaries are initialized by the static constructor for this class.
             * They describe for each combination of input elements (the key) propagation rules, which 
             * are expressions over the input expressions.
             * */
            private static readonly Dictionary s_innerJoinInsertRules;
            private static readonly Dictionary s_innerJoinDeleteRules; 
            private static readonly Dictionary s_leftOuterJoinInsertRules;
            private static readonly Dictionary s_leftOuterJoinDeleteRules; 
            #endregion 

            private readonly DbJoinExpression m_joinExpression; 
            private readonly Propagator m_parent;
            private readonly Dictionary m_insertRules;
            private readonly Dictionary m_deleteRules;
            private readonly ReadOnlyCollection m_leftKeySelectors; 
            private readonly ReadOnlyCollection m_rightKeySelectors;
            private readonly ChangeNode m_left; 
            private readonly ChangeNode m_right; 
            private readonly CompositeKey m_leftPlaceholderKey;
            private readonly CompositeKey m_rightPlaceholderKey; 
            #endregion

            #region Methods
            ///  
            /// Initialize rules.
            ///  
            static JoinPropagator() 
            {
                s_innerJoinInsertRules = new Dictionary(EqualityComparer.Default); 
                s_innerJoinDeleteRules = new Dictionary(EqualityComparer.Default);
                s_leftOuterJoinInsertRules = new Dictionary(EqualityComparer.Default);
                s_leftOuterJoinDeleteRules = new Dictionary(EqualityComparer.Default);
 
                #region Initialize propagation rules
                // These rules are taken from the mapping.update.design.doc, Section 3.5.1.3 
                // 
                InitializeRule(Ops.LeftUpdate | Ops.RightUpdate,
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.LeftDeleteJoinRightDelete,
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.LeftDeleteJoinRightDelete); 

                InitializeRule(Ops.LeftDelete | Ops.RightDelete, 
                    Ops.Nothing,
                    Ops.LeftDeleteJoinRightDelete,
                    Ops.Nothing,
                    Ops.LeftDeleteJoinRightDelete); 

                InitializeRule(Ops.LeftInsert | Ops.RightInsert, 
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.Nothing,
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.Nothing);

                InitializeRule(Ops.LeftUpdate,
                    Ops.LeftInsertUnknownExtended, 
                    Ops.LeftDeleteUnknownExtended,
                    Ops.LeftInsertUnknownExtended, 
                    Ops.LeftDeleteUnknownExtended); 

                InitializeRule(Ops.RightUpdate, 
                    Ops.RightInsertUnknownExtended,
                    Ops.RightDeleteUnknownExtended,
                    Ops.RightInsertUnknownExtended,
                    Ops.RightDeleteUnknownExtended); 

                InitializeRule(Ops.LeftUpdate | Ops.RightDelete, 
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.LeftInsertNullModifiedExtended, 
                    Ops.LeftDeleteJoinRightDelete);

                InitializeRule(Ops.LeftUpdate | Ops.RightInsert,
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.LeftDeleteNullModifiedExtended); 

                InitializeRule(Ops.LeftDelete, 
                    Ops.Unsupported,
                    Ops.Unsupported,
                    Ops.Nothing,
                    Ops.LeftDeleteNullPreserveExtended); 

                InitializeRule(Ops.LeftInsert, 
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.LeftInsertNullModifiedExtended, 
                    Ops.Nothing);

                InitializeRule(Ops.RightDelete,
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.LeftUnknownNullModifiedExtended, 
                    Ops.RightDeleteUnknownExtended); 

                InitializeRule(Ops.RightInsert, 
                    Ops.Unsupported,
                    Ops.Unsupported,
                    Ops.RightInsertUnknownExtended,
                    Ops.LeftUnknownNullModifiedExtended); 

                InitializeRule(Ops.LeftDelete | Ops.RightUpdate, 
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.Unsupported, 
                    Ops.Unsupported);

                InitializeRule(Ops.LeftDelete | Ops.RightInsert,
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.Unsupported, 
                    Ops.Unsupported); 

                InitializeRule(Ops.LeftInsert | Ops.RightUpdate, 
                    Ops.Unsupported,
                    Ops.Unsupported,
                    Ops.Unsupported,
                    Ops.Unsupported); 

                InitializeRule(Ops.LeftInsert | Ops.RightDelete, 
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.Unsupported, 
                    Ops.Unsupported);
                #endregion
            }
 
            /// 
            /// Initializes propagation rules for a specific input combination. 
            ///  
            /// Describes the elements available in the input
            /// Describes the rule for inserts when the operator is an inner join 
            /// Describes the rule for deletes when the operator is an inner join
            /// Describes the rule for inserts when the operator is a left outer join
            /// Describes the rule for deletes when the operator is a left outer join
            private static void InitializeRule(Ops input, Ops joinInsert, Ops joinDelete, Ops lojInsert, Ops lojDelete) 
            {
                s_innerJoinInsertRules.Add(input, joinInsert); 
                s_innerJoinDeleteRules.Add(input, joinDelete); 
                s_leftOuterJoinInsertRules.Add(input, lojInsert);
                s_leftOuterJoinDeleteRules.Add(input, lojDelete); 

                // Ensure that the right hand side of each rule contains no requests for specific row values
                // that are not also in the input.
                Debug.Assert((((joinInsert | joinDelete | lojInsert | lojDelete) & 
                    (Ops.LeftInsert | Ops.LeftDelete | Ops.RightInsert | Ops.RightDelete)) & (~input)) == Ops.Nothing,
                    "(Update/JoinPropagator/Initialization) Rules can't use unavailable data"); 
 
                // An unknown value can appear in both the delete and insert rule result or neither.
                Debug.Assert(((joinInsert ^ joinDelete) & (Ops.LeftUnknown | Ops.RightUnknown)) == Ops.Nothing && 
                    ((lojInsert ^ lojDelete) & (Ops.LeftUnknown | Ops.RightUnknown)) == Ops.Nothing,
                    "(Update/JoinPropagator/Initialization) Unknowns must appear in both delete and insert rules " +
                    "or in neither (in other words, for updates only)");
            } 

            ///  
            /// Performs join propagation. 
            /// 
            /// Changes propagated to the current join node in the update mapping view. 
            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;
            } 

            /// 
            /// Propagate all changes associated with a particular join key.
            ///  
            /// Key.
            /// Resulting changes are added to this result. 
            private void Propagate(CompositeKey key, ChangeNode result, JoinDictionary leftDeletes, JoinDictionary leftInserts, 
                JoinDictionary rightDeletes, JoinDictionary rightInserts)
            { 
                // Retrieve changes associates with this join key
                Tuple leftInsert = null;
                Tuple leftDelete = null;
                Tuple rightInsert = null; 
                Tuple 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 stateEntries = new List(); 
                    Action> 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)); 
                }
            }

            ///  
            /// Produce a tuple containing joined rows.
            ///  
            /// Left row. 
            /// Right row.
            /// Key used to join left element. 
            /// Key used to join right element.
            /// Result change node; used for type information.
            /// Result of joining the input rows.
            private PropagatorResult CreateResultTuple(Tuple left, Tuple right, ChangeNode result) 
            {
                // using ref compare to avoid triggering value based 
                CompositeKey leftKey = left.Item1; 
                CompositeKey rightKey = right.Item1;
                Dictionary 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();
                    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;
            } 

            ///  
            /// Constructs a new placeholder record for the left hand side of the join. Values taken 
            /// from the join key are injected into the record.
            ///  
            /// Key producing the left hand side.
            /// Mode used to populate the placeholder
            /// Record corresponding to the type of the left input to the join. Each value in
            /// the record is flagged as  except when it is 
            /// a component of the key.
            private PropagatorResult LeftPlaceholder(CompositeKey key, PopulateMode mode) 
            { 
                return PlaceholderPopulator.Populate(m_left.Placeholder, key, m_leftPlaceholderKey, mode, m_parent.UpdateTranslator);
            } 

            /// 
            /// See 
            ///  
            /// 
            ///  
            ///  
            private PropagatorResult RightPlaceholder(CompositeKey key, PopulateMode mode)
            { 

                return PlaceholderPopulator.Populate(m_right.Placeholder, key, m_rightPlaceholderKey, mode, m_parent.UpdateTranslator);
            }
 
            /// 
            /// Produces a hash table of all instances and processes join keys, adding them to the list 
            /// of keys handled by this node. 
            /// 
            /// List of instances (whether delete or insert) for this node. 
            /// Selectors for key components.
            /// A map from join keys to instances.
            private JoinDictionary ProcessKeys(IEnumerable instances, ReadOnlyCollection 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; 
            }
 
            // extracts key values from row expression
            private static CompositeKey ExtractKey(PropagatorResult change, ReadOnlyCollection 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);
            }
            #endregion 

            #region Nested types 
            ///  
            /// Flags indicating which change elements are available (0-4) and propagation
            /// rules (0, 5-512) 
            /// 
            [Flags]
            enum Ops : uint
            { 
                Nothing = 0,
                LeftInsert = 1, 
                LeftDelete = 2, 
                RightInsert = 4,
                RightDelete = 8, 
                LeftUnknown = 32,
                RightNullModified = 128,
                RightNullPreserve = 256,
                RightUnknown = 512, 
                LeftUpdate = LeftInsert | LeftDelete,
                RightUpdate = RightInsert | RightDelete, 
                Unsupported = 4096, 
                #region Propagation rule descriptions
                LeftInsertJoinRightInsert = LeftInsert | RightInsert, 
                LeftDeleteJoinRightDelete = LeftDelete | RightDelete,
                LeftInsertNullModifiedExtended = LeftInsert | RightNullModified,
                LeftInsertNullPreserveExtended = LeftInsert | RightNullPreserve,
                LeftInsertUnknownExtended = LeftInsert | RightUnknown, 
                LeftDeleteNullModifiedExtended = LeftDelete | RightNullModified,
                LeftDeleteNullPreserveExtended = LeftDelete | RightNullPreserve, 
                LeftDeleteUnknownExtended = LeftDelete | RightUnknown, 
                LeftUnknownNullModifiedExtended = LeftUnknown | RightNullModified,
                LeftUnknownNullPreserveExtended = LeftUnknown | RightNullPreserve, 
                RightInsertUnknownExtended = LeftUnknown | RightInsert,
                RightDeleteUnknownExtended = LeftUnknown | RightDelete,
                #endregion
            } 
            #endregion
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Data.Common.CommandTrees; 
using System.Collections.Generic;
using System.Data.Common.Utils;
using System.Diagnostics;
using System.Data.Metadata.Edm; 
using System.Data.Entity;
using System.Linq; 
using System.Collections.ObjectModel; 
namespace System.Data.Mapping.Update.Internal
{ 
    // We use CompositeKey on both sides of the dictionary because it is used both to identify rows that should be
    // joined (the Key part) and to carry context about the rows being joined (e.g. which components of the row
    // correspond to the join key).
    using JoinDictionary = Dictionary>; 
    internal partial class Propagator
    { 
        ///  
        /// Performs join propagation. The basic strategy is to identify changes (inserts, deletes)
        /// on either side of the join that are related according to the join criteria. Support is restricted 
        /// to conjunctions of equality predicates of the form left property == right property.
        /// When a group of related changes is identified, rules are applied based on the existence of
        /// different components (e.g., a left insert + right insert).
        ///  
        /// 
        /// The joins handled by this class are degenerate in the sense that a row in the 'left' input always 
        /// joins with at most one row in the 'right' input. The restrictions that allow for this assumption 
        /// are described in the update design spec (see 'Level 5 Optimization').
        ///  
        /// 
        /// Propagation rules for joins are stored in static fields of the class (initialized in the static
        /// constructor for the class).
        ///  
        private partial class JoinPropagator
        { 
            #region Constructors 
            /// 
            /// Constructs a join propagator. 
            /// 
            /// Result of propagating changes in the left input to the join
            /// Result of propagating changes in the right input to the join
            /// Join operator in update mapping view over which to propagate changes 
            /// Handler of propagation for the entire update mapping view
            internal JoinPropagator(ChangeNode left, ChangeNode right, DbJoinExpression node, Propagator parent) 
            { 
                EntityUtil.CheckArgumentNull(left, "left");
                EntityUtil.CheckArgumentNull(right, "right"); 
                EntityUtil.CheckArgumentNull(node, "node");
                EntityUtil.CheckArgumentNull(parent, "parent");

                m_left = left; 
                m_right = right;
                m_joinExpression = node; 
                m_parent = parent; 

                Debug.Assert(DbExpressionKind.LeftOuterJoin == node.ExpressionKind || DbExpressionKind.InnerJoin == node.ExpressionKind, "(Update/JoinPropagagtor/JoinEvaluator) " + 
                    "caller must ensure only left outer and inner joins are requested");
                // Retrieve propagation rules for the join type of the expression.
                if (DbExpressionKind.InnerJoin == m_joinExpression.ExpressionKind)
                { 
                    m_insertRules = s_innerJoinInsertRules;
                    m_deleteRules = s_innerJoinDeleteRules; 
                } 
                else
                { 
                    m_insertRules = s_leftOuterJoinInsertRules;
                    m_deleteRules = s_leftOuterJoinDeleteRules;
                }
 
                // Figure out key selectors involved in the equi-join (if it isn't an equi-join, we don't support it)
                JoinConditionVisitor.GetKeySelectors(node.JoinCondition, out m_leftKeySelectors, out m_rightKeySelectors); 
 
                // Find the key selector expressions in the left and right placeholders
                m_leftPlaceholderKey = ExtractKey(m_left.Placeholder, m_leftKeySelectors, m_parent); 
                m_rightPlaceholderKey = ExtractKey(m_right.Placeholder, m_rightKeySelectors, m_parent);
            }
            #endregion
 
            #region Fields
            #region Propagation rules 
            /** 
             * These static dictionaries are initialized by the static constructor for this class.
             * They describe for each combination of input elements (the key) propagation rules, which 
             * are expressions over the input expressions.
             * */
            private static readonly Dictionary s_innerJoinInsertRules;
            private static readonly Dictionary s_innerJoinDeleteRules; 
            private static readonly Dictionary s_leftOuterJoinInsertRules;
            private static readonly Dictionary s_leftOuterJoinDeleteRules; 
            #endregion 

            private readonly DbJoinExpression m_joinExpression; 
            private readonly Propagator m_parent;
            private readonly Dictionary m_insertRules;
            private readonly Dictionary m_deleteRules;
            private readonly ReadOnlyCollection m_leftKeySelectors; 
            private readonly ReadOnlyCollection m_rightKeySelectors;
            private readonly ChangeNode m_left; 
            private readonly ChangeNode m_right; 
            private readonly CompositeKey m_leftPlaceholderKey;
            private readonly CompositeKey m_rightPlaceholderKey; 
            #endregion

            #region Methods
            ///  
            /// Initialize rules.
            ///  
            static JoinPropagator() 
            {
                s_innerJoinInsertRules = new Dictionary(EqualityComparer.Default); 
                s_innerJoinDeleteRules = new Dictionary(EqualityComparer.Default);
                s_leftOuterJoinInsertRules = new Dictionary(EqualityComparer.Default);
                s_leftOuterJoinDeleteRules = new Dictionary(EqualityComparer.Default);
 
                #region Initialize propagation rules
                // These rules are taken from the mapping.update.design.doc, Section 3.5.1.3 
                // 
                InitializeRule(Ops.LeftUpdate | Ops.RightUpdate,
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.LeftDeleteJoinRightDelete,
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.LeftDeleteJoinRightDelete); 

                InitializeRule(Ops.LeftDelete | Ops.RightDelete, 
                    Ops.Nothing,
                    Ops.LeftDeleteJoinRightDelete,
                    Ops.Nothing,
                    Ops.LeftDeleteJoinRightDelete); 

                InitializeRule(Ops.LeftInsert | Ops.RightInsert, 
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.Nothing,
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.Nothing);

                InitializeRule(Ops.LeftUpdate,
                    Ops.LeftInsertUnknownExtended, 
                    Ops.LeftDeleteUnknownExtended,
                    Ops.LeftInsertUnknownExtended, 
                    Ops.LeftDeleteUnknownExtended); 

                InitializeRule(Ops.RightUpdate, 
                    Ops.RightInsertUnknownExtended,
                    Ops.RightDeleteUnknownExtended,
                    Ops.RightInsertUnknownExtended,
                    Ops.RightDeleteUnknownExtended); 

                InitializeRule(Ops.LeftUpdate | Ops.RightDelete, 
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.LeftInsertNullModifiedExtended, 
                    Ops.LeftDeleteJoinRightDelete);

                InitializeRule(Ops.LeftUpdate | Ops.RightInsert,
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.LeftInsertJoinRightInsert, 
                    Ops.LeftDeleteNullModifiedExtended); 

                InitializeRule(Ops.LeftDelete, 
                    Ops.Unsupported,
                    Ops.Unsupported,
                    Ops.Nothing,
                    Ops.LeftDeleteNullPreserveExtended); 

                InitializeRule(Ops.LeftInsert, 
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.LeftInsertNullModifiedExtended, 
                    Ops.Nothing);

                InitializeRule(Ops.RightDelete,
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.LeftUnknownNullModifiedExtended, 
                    Ops.RightDeleteUnknownExtended); 

                InitializeRule(Ops.RightInsert, 
                    Ops.Unsupported,
                    Ops.Unsupported,
                    Ops.RightInsertUnknownExtended,
                    Ops.LeftUnknownNullModifiedExtended); 

                InitializeRule(Ops.LeftDelete | Ops.RightUpdate, 
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.Unsupported, 
                    Ops.Unsupported);

                InitializeRule(Ops.LeftDelete | Ops.RightInsert,
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.Unsupported, 
                    Ops.Unsupported); 

                InitializeRule(Ops.LeftInsert | Ops.RightUpdate, 
                    Ops.Unsupported,
                    Ops.Unsupported,
                    Ops.Unsupported,
                    Ops.Unsupported); 

                InitializeRule(Ops.LeftInsert | Ops.RightDelete, 
                    Ops.Unsupported, 
                    Ops.Unsupported,
                    Ops.Unsupported, 
                    Ops.Unsupported);
                #endregion
            }
 
            /// 
            /// Initializes propagation rules for a specific input combination. 
            ///  
            /// Describes the elements available in the input
            /// Describes the rule for inserts when the operator is an inner join 
            /// Describes the rule for deletes when the operator is an inner join
            /// Describes the rule for inserts when the operator is a left outer join
            /// Describes the rule for deletes when the operator is a left outer join
            private static void InitializeRule(Ops input, Ops joinInsert, Ops joinDelete, Ops lojInsert, Ops lojDelete) 
            {
                s_innerJoinInsertRules.Add(input, joinInsert); 
                s_innerJoinDeleteRules.Add(input, joinDelete); 
                s_leftOuterJoinInsertRules.Add(input, lojInsert);
                s_leftOuterJoinDeleteRules.Add(input, lojDelete); 

                // Ensure that the right hand side of each rule contains no requests for specific row values
                // that are not also in the input.
                Debug.Assert((((joinInsert | joinDelete | lojInsert | lojDelete) & 
                    (Ops.LeftInsert | Ops.LeftDelete | Ops.RightInsert | Ops.RightDelete)) & (~input)) == Ops.Nothing,
                    "(Update/JoinPropagator/Initialization) Rules can't use unavailable data"); 
 
                // An unknown value can appear in both the delete and insert rule result or neither.
                Debug.Assert(((joinInsert ^ joinDelete) & (Ops.LeftUnknown | Ops.RightUnknown)) == Ops.Nothing && 
                    ((lojInsert ^ lojDelete) & (Ops.LeftUnknown | Ops.RightUnknown)) == Ops.Nothing,
                    "(Update/JoinPropagator/Initialization) Unknowns must appear in both delete and insert rules " +
                    "or in neither (in other words, for updates only)");
            } 

            ///  
            /// Performs join propagation. 
            /// 
            /// Changes propagated to the current join node in the update mapping view. 
            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;
            } 

            /// 
            /// Propagate all changes associated with a particular join key.
            ///  
            /// Key.
            /// Resulting changes are added to this result. 
            private void Propagate(CompositeKey key, ChangeNode result, JoinDictionary leftDeletes, JoinDictionary leftInserts, 
                JoinDictionary rightDeletes, JoinDictionary rightInserts)
            { 
                // Retrieve changes associates with this join key
                Tuple leftInsert = null;
                Tuple leftDelete = null;
                Tuple rightInsert = null; 
                Tuple 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 stateEntries = new List(); 
                    Action> 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)); 
                }
            }

            ///  
            /// Produce a tuple containing joined rows.
            ///  
            /// Left row. 
            /// Right row.
            /// Key used to join left element. 
            /// Key used to join right element.
            /// Result change node; used for type information.
            /// Result of joining the input rows.
            private PropagatorResult CreateResultTuple(Tuple left, Tuple right, ChangeNode result) 
            {
                // using ref compare to avoid triggering value based 
                CompositeKey leftKey = left.Item1; 
                CompositeKey rightKey = right.Item1;
                Dictionary 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();
                    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;
            } 

            ///  
            /// Constructs a new placeholder record for the left hand side of the join. Values taken 
            /// from the join key are injected into the record.
            ///  
            /// Key producing the left hand side.
            /// Mode used to populate the placeholder
            /// Record corresponding to the type of the left input to the join. Each value in
            /// the record is flagged as  except when it is 
            /// a component of the key.
            private PropagatorResult LeftPlaceholder(CompositeKey key, PopulateMode mode) 
            { 
                return PlaceholderPopulator.Populate(m_left.Placeholder, key, m_leftPlaceholderKey, mode, m_parent.UpdateTranslator);
            } 

            /// 
            /// See 
            ///  
            /// 
            ///  
            ///  
            private PropagatorResult RightPlaceholder(CompositeKey key, PopulateMode mode)
            { 

                return PlaceholderPopulator.Populate(m_right.Placeholder, key, m_rightPlaceholderKey, mode, m_parent.UpdateTranslator);
            }
 
            /// 
            /// Produces a hash table of all instances and processes join keys, adding them to the list 
            /// of keys handled by this node. 
            /// 
            /// List of instances (whether delete or insert) for this node. 
            /// Selectors for key components.
            /// A map from join keys to instances.
            private JoinDictionary ProcessKeys(IEnumerable instances, ReadOnlyCollection 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; 
            }
 
            // extracts key values from row expression
            private static CompositeKey ExtractKey(PropagatorResult change, ReadOnlyCollection 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);
            }
            #endregion 

            #region Nested types 
            ///  
            /// Flags indicating which change elements are available (0-4) and propagation
            /// rules (0, 5-512) 
            /// 
            [Flags]
            enum Ops : uint
            { 
                Nothing = 0,
                LeftInsert = 1, 
                LeftDelete = 2, 
                RightInsert = 4,
                RightDelete = 8, 
                LeftUnknown = 32,
                RightNullModified = 128,
                RightNullPreserve = 256,
                RightUnknown = 512, 
                LeftUpdate = LeftInsert | LeftDelete,
                RightUpdate = RightInsert | RightDelete, 
                Unsupported = 4096, 
                #region Propagation rule descriptions
                LeftInsertJoinRightInsert = LeftInsert | RightInsert, 
                LeftDeleteJoinRightDelete = LeftDelete | RightDelete,
                LeftInsertNullModifiedExtended = LeftInsert | RightNullModified,
                LeftInsertNullPreserveExtended = LeftInsert | RightNullPreserve,
                LeftInsertUnknownExtended = LeftInsert | RightUnknown, 
                LeftDeleteNullModifiedExtended = LeftDelete | RightNullModified,
                LeftDeleteNullPreserveExtended = LeftDelete | RightNullPreserve, 
                LeftDeleteUnknownExtended = LeftDelete | RightUnknown, 
                LeftUnknownNullModifiedExtended = LeftUnknown | RightNullModified,
                LeftUnknownNullPreserveExtended = LeftUnknown | RightNullPreserve, 
                RightInsertUnknownExtended = LeftUnknown | RightInsert,
                RightDeleteUnknownExtended = LeftUnknown | RightDelete,
                #endregion
            } 
            #endregion
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK