1 //--------------------------------------------------------------------- 2 // <copyright file="CompositeKey.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 using System.Data.Common.Utils; 11 using System.Collections.Generic; 12 using System.Collections.ObjectModel; 13 using System.Diagnostics; 14 using System.Linq; 15 namespace System.Data.Mapping.Update.Internal 16 { 17 /// <summary> 18 /// Represents a key composed of multiple parts. 19 /// </summary> 20 internal class CompositeKey 21 { 22 #region Fields 23 /// <summary> 24 /// Gets components of this composite key. 25 /// </summary> 26 internal readonly PropagatorResult[] KeyComponents; 27 #endregion 28 29 #region Constructors 30 /// <summary> 31 /// Initialize a new composite key using the given constant values. Order is important. 32 /// </summary> 33 /// <param name="values">Key values.</param> CompositeKey(PropagatorResult[] constants)34 internal CompositeKey(PropagatorResult[] constants) 35 { 36 Debug.Assert(null != constants, "key values must be given"); 37 38 KeyComponents = constants; 39 } 40 #endregion 41 42 #region Methods 43 /// <summary> 44 /// Creates a key comparer operating in the context of the given translator. 45 /// </summary> CreateComparer(KeyManager keyManager)46 internal static IEqualityComparer<CompositeKey> CreateComparer(KeyManager keyManager) 47 { 48 return new CompositeKeyComparer(keyManager); 49 } 50 51 /// <summary> 52 /// Creates a merged key instance where each key component contains both elements. 53 /// </summary> 54 /// <param name="other">Must be a non-null compatible key (same number of components).</param> 55 /// <returns>Merged key.</returns> Merge(KeyManager keyManager, CompositeKey other)56 internal CompositeKey Merge(KeyManager keyManager, CompositeKey other) 57 { 58 Debug.Assert(null != other && other.KeyComponents.Length == this.KeyComponents.Length, "expected a compatible CompositeKey"); 59 PropagatorResult[] mergedKeyValues = new PropagatorResult[this.KeyComponents.Length]; 60 for (int i = 0; i < this.KeyComponents.Length; i++) 61 { 62 mergedKeyValues[i] = this.KeyComponents[i].Merge(keyManager, other.KeyComponents[i]); 63 } 64 return new CompositeKey(mergedKeyValues); 65 } 66 #endregion 67 68 /// <summary> 69 /// Equality and comparison implementation for composite keys. 70 /// </summary> 71 private class CompositeKeyComparer : IEqualityComparer<CompositeKey> 72 { 73 private readonly KeyManager _manager; 74 CompositeKeyComparer(KeyManager manager)75 internal CompositeKeyComparer(KeyManager manager) 76 { 77 _manager = EntityUtil.CheckArgumentNull(manager, "manager"); 78 } 79 80 // determines equality by comparing each key component Equals(CompositeKey left, CompositeKey right)81 public bool Equals(CompositeKey left, CompositeKey right) 82 { 83 // Short circuit the comparison if we know the other reference is equivalent 84 if (object.ReferenceEquals(left, right)) { return true; } 85 86 // If either side is null, return false order (both can't be null because of 87 // the previous check) 88 if (null == left || null == right) { return false; } 89 90 Debug.Assert(null != left.KeyComponents && null != right.KeyComponents, 91 "(Update/JoinPropagator) CompositeKey must be initialized"); 92 93 if (left.KeyComponents.Length != right.KeyComponents.Length) { return false; } 94 95 for (int i = 0; i < left.KeyComponents.Length; i++) 96 { 97 PropagatorResult leftValue = left.KeyComponents[i]; 98 PropagatorResult rightValue = right.KeyComponents[i]; 99 100 // if both side are identifiers, check if they're the same or one is constrained by the 101 // other (if there is a dependent-principal relationship, they get fixed up to the same 102 // value) 103 if (leftValue.Identifier != PropagatorResult.NullIdentifier) 104 { 105 if (rightValue.Identifier == PropagatorResult.NullIdentifier || 106 _manager.GetCliqueIdentifier(leftValue.Identifier) != _manager.GetCliqueIdentifier(rightValue.Identifier)) 107 { 108 return false; 109 } 110 } 111 else 112 { 113 if (rightValue.Identifier != PropagatorResult.NullIdentifier || 114 !ByValueEqualityComparer.Default.Equals(leftValue.GetSimpleValue(), rightValue.GetSimpleValue())) 115 { 116 return false; 117 } 118 } 119 } 120 121 return true; 122 } 123 124 // creates a hash code by XORing hash codes for all key components. GetHashCode(CompositeKey key)125 public int GetHashCode(CompositeKey key) 126 { 127 EntityUtil.CheckArgumentNull(key, "key"); 128 129 int result = 0; 130 foreach (PropagatorResult keyComponent in key.KeyComponents) 131 { 132 result = (result << 5) ^ GetComponentHashCode(keyComponent); 133 } 134 135 return result; 136 } 137 138 // Gets the value to use for hash code GetComponentHashCode(PropagatorResult keyComponent)139 private int GetComponentHashCode(PropagatorResult keyComponent) 140 { 141 if (keyComponent.Identifier == PropagatorResult.NullIdentifier) 142 { 143 // no identifier exists for this key component, so use the actual key 144 // value 145 Debug.Assert(null != keyComponent && null != keyComponent, 146 "key value must not be null"); 147 return ByValueEqualityComparer.Default.GetHashCode(keyComponent.GetSimpleValue()); 148 } 149 else 150 { 151 // use ID for FK graph clique (this ensures that keys fixed up to the same 152 // value based on a constraint will have the same hash code) 153 return _manager.GetCliqueIdentifier(keyComponent.Identifier).GetHashCode(); 154 } 155 } 156 } 157 } 158 } 159