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