|
//---------------------------------------------------------------------
// <copyright file="CompositeKey.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
using System.Data.Common.Utils;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
namespace System.Data.Mapping.Update.Internal
{
/// <summary>
/// Represents a key composed of multiple parts.
/// </summary>
internal class CompositeKey
{
#region Fields
/// <summary>
/// Gets components of this composite key.
/// </summary>
internal readonly PropagatorResult[] KeyComponents;
#endregion
#region Constructors
/// <summary>
/// Initialize a new composite key using the given constant values. Order is important.
/// </summary>
/// <param name="values">Key values.</param>
internal CompositeKey(PropagatorResult[] constants)
{
Debug.Assert(null != constants, "key values must be given");
KeyComponents = constants;
}
#endregion
#region Methods
/// <summary>
/// Creates a key comparer operating in the context of the given translator.
/// </summary>
internal static IEqualityComparer<CompositeKey> CreateComparer(KeyManager keyManager)
{
return new CompositeKeyComparer(keyManager);
}
/// <summary>
/// Creates a merged key instance where each key component contains both elements.
/// </summary>
/// <param name="other">Must be a non-null compatible key (same number of components).</param>
/// <returns>Merged key.</returns>
internal CompositeKey Merge(KeyManager keyManager, CompositeKey other)
{
Debug.Assert(null != other && other.KeyComponents.Length == this.KeyComponents.Length, "expected a compatible CompositeKey");
PropagatorResult[] mergedKeyValues = new PropagatorResult[this.KeyComponents.Length];
for (int i = 0; i < this.KeyComponents.Length; i++)
{
mergedKeyValues[i] = this.KeyComponents[i].Merge(keyManager, other.KeyComponents[i]);
}
return new CompositeKey(mergedKeyValues);
}
#endregion
/// <summary>
/// Equality and comparison implementation for composite keys.
/// </summary>
private class CompositeKeyComparer : IEqualityComparer<CompositeKey>
{
private readonly KeyManager _manager;
internal CompositeKeyComparer(KeyManager manager)
{
_manager = EntityUtil.CheckArgumentNull(manager, "manager");
}
// determines equality by comparing each key component
public bool Equals(CompositeKey left, CompositeKey right)
{
// Short circuit the comparison if we know the other reference is equivalent
if (object.ReferenceEquals(left, right)) { return true; }
// If either side is null, return false order (both can't be null because of
// the previous check)
if (null == left || null == right) { return false; }
Debug.Assert(null != left.KeyComponents && null != right.KeyComponents,
"(Update/JoinPropagator) CompositeKey must be initialized");
if (left.KeyComponents.Length != right.KeyComponents.Length) { return false; }
for (int i = 0; i < left.KeyComponents.Length; i++)
{
PropagatorResult leftValue = left.KeyComponents[i];
PropagatorResult rightValue = right.KeyComponents[i];
// if both side are identifiers, check if they're the same or one is constrained by the
// other (if there is a dependent-principal relationship, they get fixed up to the same
// value)
if (leftValue.Identifier != PropagatorResult.NullIdentifier)
{
if (rightValue.Identifier == PropagatorResult.NullIdentifier ||
_manager.GetCliqueIdentifier(leftValue.Identifier) != _manager.GetCliqueIdentifier(rightValue.Identifier))
{
return false;
}
}
else
{
if (rightValue.Identifier != PropagatorResult.NullIdentifier ||
!ByValueEqualityComparer.Default.Equals(leftValue.GetSimpleValue(), rightValue.GetSimpleValue()))
{
return false;
}
}
}
return true;
}
// creates a hash code by XORing hash codes for all key components.
public int GetHashCode(CompositeKey key)
{
EntityUtil.CheckArgumentNull(key, "key");
int result = 0;
foreach (PropagatorResult keyComponent in key.KeyComponents)
{
result = (result << 5) ^ GetComponentHashCode(keyComponent);
}
return result;
}
// Gets the value to use for hash code
private int GetComponentHashCode(PropagatorResult keyComponent)
{
if (keyComponent.Identifier == PropagatorResult.NullIdentifier)
{
// no identifier exists for this key component, so use the actual key
// value
Debug.Assert(null != keyComponent && null != keyComponent,
"key value must not be null");
return ByValueEqualityComparer.Default.GetHashCode(keyComponent.GetSimpleValue());
}
else
{
// use ID for FK graph clique (this ensures that keys fixed up to the same
// value based on a constraint will have the same hash code)
return _manager.GetCliqueIdentifier(keyComponent.Identifier).GetHashCode();
}
}
}
}
}
|