Code:
/ 4.0 / 4.0 / untmp / 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.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RefreshInfo.cs
- BufferModeSettings.cs
- MethodBody.cs
- HtmlGenericControl.cs
- CqlLexerHelpers.cs
- _SafeNetHandles.cs
- DocumentReferenceCollection.cs
- Console.cs
- Pen.cs
- Convert.cs
- FontResourceCache.cs
- FormViewDeleteEventArgs.cs
- ZoneLinkButton.cs
- RightsController.cs
- DataColumnCollection.cs
- RemoveFromCollection.cs
- ImageListStreamer.cs
- XmlSchemaInferenceException.cs
- WebDescriptionAttribute.cs
- WindowsFormsSectionHandler.cs
- Control.cs
- UnsafeMethods.cs
- IgnoreSection.cs
- RichTextBoxAutomationPeer.cs
- WindowsFormsHelpers.cs
- CodeSnippetCompileUnit.cs
- ExeContext.cs
- ClientSettingsProvider.cs
- DropShadowBitmapEffect.cs
- Validator.cs
- AssociationSet.cs
- BuildProvider.cs
- ComponentEvent.cs
- CompletionCallbackWrapper.cs
- StaticResourceExtension.cs
- SByteConverter.cs
- ToolStripDropDownMenu.cs
- XsltArgumentList.cs
- _ConnectOverlappedAsyncResult.cs
- DesignerActionUIService.cs
- ExceptionUtil.cs
- HtmlPhoneCallAdapter.cs
- Expressions.cs
- HttpContextServiceHost.cs
- ResourceManager.cs
- TextElement.cs
- log.cs
- Empty.cs
- OperationContractAttribute.cs
- IDQuery.cs
- MemberAccessException.cs
- GenericTypeParameterBuilder.cs
- WizardPanelChangingEventArgs.cs
- GetLedgerRequest.cs
- SqlSupersetValidator.cs
- ValidatingPropertiesEventArgs.cs
- NameTable.cs
- CreatingCookieEventArgs.cs
- LicenseContext.cs
- UnitySerializationHolder.cs
- Peer.cs
- InvokeWebServiceDesigner.cs
- XmlNodeReader.cs
- DataGridViewCellMouseEventArgs.cs
- StrokeNodeData.cs
- OracleDataReader.cs
- RijndaelManagedTransform.cs
- PEFileReader.cs
- DataGridViewCell.cs
- XmlEncodedRawTextWriter.cs
- TextProperties.cs
- ADMembershipProvider.cs
- DockPattern.cs
- ProviderManager.cs
- SoapIncludeAttribute.cs
- ComPlusDiagnosticTraceRecords.cs
- MergePropertyDescriptor.cs
- DefaultProxySection.cs
- Timeline.cs
- CachedCompositeFamily.cs
- PropertyMetadata.cs
- ScriptReference.cs
- RegexCode.cs
- ResumeStoryboard.cs
- BulletedList.cs
- EditorPartCollection.cs
- SQLDecimalStorage.cs
- ISO2022Encoding.cs
- RecordBuilder.cs
- LayoutTableCell.cs
- ApplyImportsAction.cs
- PenThread.cs
- UxThemeWrapper.cs
- DocumentSchemaValidator.cs
- Rect.cs
- TextServicesCompartment.cs
- CodeCastExpression.cs
- MulticastOption.cs
- ControlBuilderAttribute.cs
- GenericPrincipal.cs