EntityKey.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / EntityKey.cs / 4 / EntityKey.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System.Collections.Generic;
using System.Collections.ObjectModel; 
using System.Data;
using System.Diagnostics;
using System.Collections;
using System.Data.Common; 
using System.Data.Common.Utils;
using System.Data.Metadata.Edm; 
using System.Data.Mapping; 
using System.Data.Objects;
using System.Data.Common.CommandTrees; 
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.Xml;
 
namespace System.Data
{ 
    ///  
    /// An identifier for an entity.
    ///  
    [DebuggerDisplay("{ConcatKeyValue()}")]
    [Serializable]
    [DataContract(IsReference=true)]
    public sealed class EntityKey : IEquatable 
    {
        // The implementation of EntityKey is optimized for the following common cases: 
        //      1) Keys constructed internally rather by the user - in particular, keys 
        //         created by the bridge on the round-trip from query.
        //      2) Single-valued (as opposed to composite) keys. 
        // We accomplish this by maintaining two variables, at most one of which is non-null.
        // The first is of type object and in the case of a singleton key, is set to the
        // single key value.  The second is an object array and in the case of
        // a composite key, is set to the list of key values.  If both variables are null, 
        // the EntityKey is a temporary key.  Note that the key field names
        // are not stored - for composite keys, the values are stored in the order in which 
        // metadata reports the corresponding key members. 

        // The following 5 fields are serialized.  Adding or removing a serialized field is considered 
        // a breaking change.  This includes changing the field type or field name of existing
        // serialized fields. If you need to make this kind of change, it may be possible, but it
        // will require some custom serialization/deserialization code.
        private string _entitySetName; 
        private string _entityContainerName;
        private object _singletonKeyValue;      // non-null for singleton keys 
        private object[] _compositeKeyValues;   // non-null for composite keys 
        private string[] _keyNames;             // key names that correspond to the key values
        private bool _isLocked;                 // determines if this key is lock from writing 

        [NonSerialized]
        private EntityKeyMember[] _deserializedMembers;
 
        // The hash code is not serialized since it can be computed differently on the deserialized system.
        [NonSerialized] 
        private int _hashCode;                  // computed as needed 

 
        // Names for constant EntityKeys
        private const string s_NoEntitySetKey = "NoEntitySetKey.NoEntitySetKey";
        private const string s_EntityNotValidKey = "EntityNotValidKey.EntityNotValidKey";
 
        /// 
        /// A singleton EntityKey by which a readonly entity is identified. 
        ///  
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]     // Justification: these are internal so they cannot be modified publically
        public static readonly EntityKey NoEntitySetKey = new EntityKey(s_NoEntitySetKey); 

        /// 
        /// A singleton EntityKey identifying an entity resulted from a failed TREAT.
        ///  
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]     // Justification: these are internal so they cannot be modified publically
        public static readonly EntityKey EntityNotValidKey = new EntityKey(s_EntityNotValidKey); 
 
        /// 
        /// A dictionary of names so that singleton instances of names can be used 
        /// 
        private static Dictionary _nameLookup = new Dictionary();

        #region Public Constructors 

        ///  
        /// Constructs an empty EntityKey. For use during XmlSerialization. 
        /// 
        public EntityKey() 
        {
            _isLocked = false;
        }
 
        /// 
        /// Constructs an EntityKey with the given key values. 
        ///  
        /// The EntitySet name, qualified by the EntityContainer name, of the entity
        /// The key-value pairs that identify the entity 
        public EntityKey(string qualifiedEntitySetName, IEnumerable> entityKeyValues)
        {
            GetEntitySetName(qualifiedEntitySetName, out _entitySetName, out _entityContainerName);
            CheckKeyValues(entityKeyValues, out _keyNames, out _singletonKeyValue, out _compositeKeyValues); 
            AssertCorrectState(null, false);
            _isLocked = true; 
        } 

        ///  
        /// Constructs an EntityKey with the given key values.
        /// 
        /// The EntitySet name, qualified by the EntityContainer name, of the entity
        /// The key-value pairs that identify the entity 
        public EntityKey(string qualifiedEntitySetName, IEnumerable entityKeyValues)
        { 
            GetEntitySetName(qualifiedEntitySetName, out _entitySetName, out _entityContainerName); 
            CheckKeyValues(new KeyValueReader(entityKeyValues), out _keyNames, out _singletonKeyValue, out _compositeKeyValues);
            AssertCorrectState(null, false); 
            _isLocked = true;
        }

        ///  
        /// Constructs an EntityKey with the given single key name and value.
        ///  
        /// The EntitySet name, qualified by the EntityContainer name, of the entity 
        /// The key name that identifies the entity
        /// The key value that identifies the entity 
        public EntityKey(string qualifiedEntitySetName, string keyName, object keyValue)
        {
            GetEntitySetName(qualifiedEntitySetName, out _entitySetName, out _entityContainerName);
            EntityUtil.CheckStringArgument(keyName, "keyName"); 
            EntityUtil.CheckArgumentNull(keyValue, "keyValue");
 
            _keyNames = new string[1]; 
            ValidateName(keyName);
            _keyNames[0] = keyName; 
            _singletonKeyValue = keyValue;

            AssertCorrectState(null, false);
            _isLocked = true; 
        }
 
        #endregion 

        #region Internal Constructors 

        /// 
        /// Constructs an EntityKey from an IExtendedDataRecord representing the entity.
        ///  
        /// EntitySet of the entity
        /// an IExtendedDataRecord that represents the entity 
        internal EntityKey(EntitySet entitySet, IExtendedDataRecord record) 
        {
            EntityUtil.CheckArgumentNull(entitySet, "entitySet"); 
            EntityUtil.CheckArgumentNull(entitySet.Name, "entitySet.Name");
            EntityUtil.CheckArgumentNull(entitySet.EntityContainer, "entitySet.Container");
            EntityUtil.CheckArgumentNull(entitySet.EntityContainer.Name, "entitySet.Container.Name");
            EntityUtil.CheckArgumentNull(record, "record"); 
            _entitySetName = entitySet.Name;
            _entityContainerName = entitySet.EntityContainer.Name; 
 
            CheckKeyValues(entitySet, record, out _keyNames, out _singletonKeyValue, out _compositeKeyValues);
            AssertCorrectState(entitySet, false); 
            _isLocked = true;
        }

        ///  
        /// Constructs an EntityKey from an IExtendedDataRecord representing the entity.
        ///  
        /// EntitySet of the entity 
        /// an IExtendedDataRecord that represents the entity
        internal EntityKey(string qualifiedEntitySetName) 
        {
            GetEntitySetName(qualifiedEntitySetName, out _entitySetName, out _entityContainerName);
            _isLocked = true;
        } 

        ///  
        /// Constructs a temporary EntityKey with the given EntitySet. 
        /// Temporary keys do not store key field names
        ///  
        /// EntitySet of the entity
        internal EntityKey(EntitySetBase entitySet)
        {
            EntityUtil.CheckArgumentNull(entitySet, "entitySet"); 
            Debug.Assert(entitySet.EntityContainer != null, "EntitySet.EntityContainer cannot be null.");
 
            _entitySetName = entitySet.Name; 
            _entityContainerName = entitySet.EntityContainer.Name;
 
            AssertCorrectState(entitySet, true);
            _isLocked = true;
        }
 
        /// 
        /// Constructor optimized for a singleton key. 
        /// SQLBUDT 478655: Performance optimization: Does no integrity checking on the key value. 
        /// SQLBUDT 523554: Performance optimization: Does no validate type of key members.
        ///  
        /// EntitySet of the entity
        /// The single value that composes the entity's key, assumed to contain the correct type.
        internal EntityKey(EntitySetBase entitySet, object singletonKeyValue)
        { 
            Debug.Assert(entitySet != null, "EntitySet cannot be null.");
            Debug.Assert(entitySet.EntityContainer != null, "EntitySet.EntityContainer cannot be null."); 
            Debug.Assert(singletonKeyValue != null, "Singleton key value cannot be null."); 
            _singletonKeyValue = singletonKeyValue;
            _entitySetName = entitySet.Name; 
            _entityContainerName = entitySet.EntityContainer.Name;
            _keyNames = entitySet.ElementType.KeyMemberNames; // using EntitySetBase avoids an (EntityType) cast that EntitySet encoure
            AssertCorrectState(entitySet, false);
            _isLocked = true; 
        }
 
        ///  
        /// Constructor optimized for a composite key.
        /// SQLBUDT 478655: Performance optimization: Does no integrity checking on the key values. 
        /// SQLBUDT 523554: Performance optimization: Does no validate type of key members.
        /// 
        /// EntitySet of the entity
        /// A list of the values (at least 2) that compose the entity's key, assumed to contain correct types. 
        internal EntityKey(EntitySetBase entitySet, object[] compositeKeyValues)
        { 
            Debug.Assert(entitySet != null, "EntitySet cannot be null."); 
            Debug.Assert(entitySet.EntityContainer != null, "EntitySet.EntityContainer cannot be null.");
            Debug.Assert(compositeKeyValues != null, "Composite key values cannot be null."); 
            _compositeKeyValues = compositeKeyValues;
            _entitySetName = entitySet.Name;
            _entityContainerName = entitySet.EntityContainer.Name;
            _keyNames = entitySet.ElementType.KeyMemberNames; // using EntitySetBase avoids an (EntityType) cast that EntitySet encoure 
            AssertCorrectState(entitySet, false);
            _isLocked = true; 
        } 

        #endregion 

        /// 
        /// Gets the EntitySet name identifying the entity set that contains the entity.
        ///  
        [DataMember]
        public string EntitySetName 
        { 
            get
            { 
                return _entitySetName;
            }
            set
            { 
                ValidateWritable(_entitySetName);
                lock(_nameLookup) 
                { 
                    _entitySetName = EntityKey.LookupSingletonName(value);
                } 
            }
        }

        ///  
        /// Gets the EntityContainer name identifying the entity container that contains the entity.
        ///  
        [DataMember] 
        public string EntityContainerName
        { 
            get
            {
                return _entityContainerName;
            } 
            set
            { 
                ValidateWritable(_entityContainerName); 
                lock (_nameLookup)
                { 
                    _entityContainerName = EntityKey.LookupSingletonName(value);
                }
            }
        } 

 
        ///  
        /// Gets the key values that identify the entity.
        ///  
        [DataMember]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification="Required for this feature")]
        public EntityKeyMember[] EntityKeyValues
        { 
            get
            { 
                if(!IsTemporary) 
                {
                    EntityKeyMember[] keyValues; 
                    if (_singletonKeyValue != null)
                    {
                        keyValues = new EntityKeyMember[] {
                                new EntityKeyMember(_keyNames[0], _singletonKeyValue) }; 
                    }
                    else 
                    { 
                        keyValues = new EntityKeyMember[_compositeKeyValues.Length];
                        for (int i = 0; i < _compositeKeyValues.Length; ++i) 
                        {
                            keyValues[i] = new EntityKeyMember(_keyNames[i], _compositeKeyValues[i]);
                        }
                    } 
                    return keyValues;
                } 
                else 
                {
                    return null; 
                }
            }
            set
            { 
                ValidateWritable(_keyNames);
                if (value != null) 
                { 
                    if (!CheckKeyValues(new KeyValueReader(value), true, true, out _keyNames, out _singletonKeyValue, out _compositeKeyValues))
                    { 
                        // If we did not retrieve values from the setter (i.e. encoded settings), we need to keep track of the
                        // array instance because the array members will be set next.
                        _deserializedMembers = value;
                    } 
                }
            } 
        } 

        ///  
        /// Gets a value indicating whether this key is a temporary key.
        /// 
        public bool IsTemporary
        { 
            get
            { 
                return (SingletonKeyValue == null) && (CompositeKeyValues == null); 
            }
        } 

        private object SingletonKeyValue
        {
            get 
            {
                if (RequiresDeserialization) 
                { 
                    DeserializeMembers();
                } 
                return _singletonKeyValue;
            }
        }
 
        private object[] CompositeKeyValues
        { 
            get 
            {
                if (RequiresDeserialization) 
                {
                    DeserializeMembers();
                }
                return _compositeKeyValues; 
            }
        } 
 
        /// 
        /// Gets the entity set for this entity key from the given metadata workspace, by 
        /// entity container name and entity set name.
        /// 
        /// workspace in which to look up the entity set
        /// the entity set from the given workspace for this entity key 
        /// the entity set could not be located in the workspace
        public EntitySet GetEntitySet(MetadataWorkspace metadataWorkspace) 
        { 
            EntityUtil.CheckArgumentNull(metadataWorkspace, "metadataWorkspace");
            if (String.IsNullOrEmpty(_entityContainerName) || String.IsNullOrEmpty(_entitySetName)) 
            {
                throw EntityUtil.MissingQualifiedEntitySetName();
            }
 
            // GetEntityContainer will throw if it cannot find the container
 
            // SQLBUDT 479443:  If this entity key was initially created using an entity set 
            // from a different workspace, look up the entity set in the new workspace.
            // Metadata will throw an ArgumentException if the entity set could not be found. 

            return metadataWorkspace
                .GetEntityContainer(_entityContainerName, DataSpace.CSpace)
                .GetEntitySetByName(_entitySetName, false); 
        }
 
        #region Equality/Hashing 

        ///  
        /// Compares this instance to a given key by their values.
        /// 
        /// the key to compare against this instance
        /// true if this instance is equal to the given key, and false otherwise 
        public override bool Equals(object obj)
        { 
            return InternalEquals(this, obj as EntityKey); 
        }
 
        /// 
        /// Compares this instance to a given key by their values.
        /// 
        /// the key to compare against this instance 
        /// true if this instance is equal to the given key, and false otherwise
        public bool Equals(EntityKey other) 
        { 
            return InternalEquals(this, other);
        } 

        /// 
        /// Returns a value-based hash code, to allow EntityKey to be used in hash tables.
        ///  
        /// the hash value of this EntityKey
        public override int GetHashCode() 
        { 
            int hashCode = _hashCode;
            if (0 == hashCode) 
            {
                if (RequiresDeserialization)
                {
                    DeserializeMembers(); 
                }
 
                int baseHash = 0; 
                if (_entitySetName != null)
                { 
                    baseHash += _entitySetName.GetHashCode();
                }
                if (_entityContainerName != null)
                { 
                    baseHash += _entityContainerName.GetHashCode();
                } 
 
                // If the key is not temporary, determine a hash code based on the value(s) within the key.
                if (null != _singletonKeyValue) 
                {
                    hashCode = unchecked(baseHash ^ _singletonKeyValue.GetHashCode());
                }
                else if (null != _compositeKeyValues) 
                {
                    hashCode = unchecked(baseHash ^ GetHashCode(_compositeKeyValues)); 
                } 
                else
                { 
                    // If the key is temporary, use default hash code
                    hashCode = base.GetHashCode();
                }
 
                // cache the hash code if we are a locked or fully specified EntityKey
                if (_isLocked || (!String.IsNullOrEmpty(_entitySetName) && 
                                  !String.IsNullOrEmpty(_entityContainerName) && 
                                  (_singletonKeyValue != null || _compositeKeyValues != null)))
                { 
                    _hashCode = hashCode;
                }
            }
            return hashCode; 
        }
 
        private static int GetHashCode(object[] compositeKeyValues) 
        {
            int hashCode = 0; 
            for(int i = 0; i < compositeKeyValues.Length; ++i)
            {
                // For a composite key, chain-XOR each value to compute the hash code.
                // This implies that the keys for the following two objects (from the 
                // same entity set) will hash to the same value, despite being unequal.
                // This isn't a bug, just a known hash collision. 
                //      Object1: 
                //          KeyField1 = 10;
                //          KeyField2 = 20; 
                //      Object2:
                //          KeyField1 = 20;
                //          KeyField2 = 10;
                unchecked { 
                    hashCode += compositeKeyValues[i].GetHashCode();
                } 
            } 
            return hashCode;
        } 

        /// 
        /// Compares two keys by their values.
        ///  
        /// a key to compare
        /// a key to compare 
        /// true if the two keys are equal, false otherwise 
        public static bool operator ==(EntityKey key1, EntityKey key2)
        { 
#if DEBUG
            if (((object)NoEntitySetKey == (object)key1) || ((object)EntityNotValidKey == (object)key1) ||
                ((object)NoEntitySetKey == (object)key2) || ((object)EntityNotValidKey == (object)key1)
                // || (null==(object)key1) || (null==(object)key2)) //To check for internal use of null==key 
                )
            { 
                Debug.Assert(typeof(EntityKey).Assembly != System.Reflection.Assembly.GetCallingAssembly(), "When comparing an EntityKey to one of the predefined types (EntityKey.NoEntitySetKey or EntityKey.EntityNotValidKey), use Object.ReferenceEquals()"); 
            }
#endif 
            return InternalEquals(key1, key2);
        }

        ///  
        /// Compares two keys by their values.
        ///  
        /// a key to compare 
        /// a key to compare
        /// true if the two keys are not equal, false otherwise 
        public static bool operator !=(EntityKey key1, EntityKey key2)
        {
#if DEBUG
            if (((object)NoEntitySetKey == (object)key1) || ((object)EntityNotValidKey == (object)key1) || 
                ((object)NoEntitySetKey == (object)key2) || ((object)EntityNotValidKey == (object)key1))
            // || (null==(object)key1) || (null==(object)key2)) //To check for internal use of null==key 
            { 
                Debug.Assert(typeof(EntityKey).Assembly != System.Reflection.Assembly.GetCallingAssembly(), "When comparing an EntityKey to one of the predefined types (EntityKey.NoEntitySetKey or EntityKey.EntityNotValidKey), use Object.ReferenceEquals()");
            } 
#endif
            return !InternalEquals(key1, key2);
        }
 
        /// 
        /// Internal function to compare two keys by their values. 
        ///  
        /// a key to compare
        /// a key to compare 
        /// true if the two keys are equal, false otherwise
        private static bool InternalEquals(EntityKey key1, EntityKey key2)
        {
            // If both are null or refer to the same object, they're equal. 
            if (object.ReferenceEquals(key1, key2))
            { 
                return true; 
            }
 
            // If exactly one is null (avoid calling EntityKey == operator overload), they're not equal.
            if (object.ReferenceEquals(key1, null) || object.ReferenceEquals(key2, null))
            {
                return false; 
            }
 
            if (key1.RequiresDeserialization) 
            {
                key1.DeserializeMembers(); 
            }
            else if (key2.RequiresDeserialization)
            {
                key2.DeserializeMembers(); 
            }
 
            // If the hash codes differ, the keys are not equal.  Note that 
            // a key's hash code is cached after being computed for the first time,
            // so this check will only incur the cost of computing a hash code 
            // at most once for a given key.

            // The primary caller is Dictionary
            // at which point Equals is only called after HashCode was deteremined to be equal 
            //if (key1.GetHashCode() != key2.GetHashCode())
            //{ 
            //    return false; 
            //}
 
            if (null != key1._singletonKeyValue)
            {
                // Compare the single value (if the second is null, false should be returned)
                return (key1._singletonKeyValue.Equals(key2._singletonKeyValue) && 
                        String.Equals(key1._keyNames[0], key2._keyNames[0]) &&
                        String.Equals(key1._entityContainerName, key2._entityContainerName) && 
                        String.Equals(key1._entitySetName, key2._entitySetName)); 
            }
 
            // If either key is temporary, they're not equal.  This is because
            // temporary keys are compared by CLR reference, and we've already
            // checked reference equality.
            // If the first key is a composite key and the second one isn't, they're not equal. 

            // They're both composite keys, so compare each value. 
            return ((null != key1._compositeKeyValues) && 
                    (null != key2._compositeKeyValues) &&
                    String.Equals(key1._entityContainerName, key2._entityContainerName) && 
                    String.Equals(key1._entitySetName, key2._entitySetName) &&
                    (key1._compositeKeyValues.Length == key2._compositeKeyValues.Length) &&
                    ComplexKeyValuesEqual(key1, key2));
        } 

        private static bool ComplexKeyValuesEqual(EntityKey key1, EntityKey key2) 
        { 
            for (int i = 0; i < key1._compositeKeyValues.Length; ++i)
            { 
                if (key1._keyNames[i].Equals(key2._keyNames[i]))
                {
                    if (!Object.Equals(key1._compositeKeyValues[i], key2._compositeKeyValues[i]))
                    { 
                        return false;
                    } 
                } 
                else
                { 
                    // Key names might not be in the same order so try a slower approach that matches
                    // key names between the keys.
                    return KeyValuesEqual(key1, key2);
                } 
            }
            return true; 
        } 

        private static bool KeyValuesEqual(EntityKey key1, EntityKey key2) 
        {
            Dictionary values = new Dictionary();
            foreach (EntityKeyMember key1Value in key1.EntityKeyValues)
            { 
                values.Add(key1Value.Key, key1Value.Value);
            } 
            foreach (EntityKeyMember key2Value in key2.EntityKeyValues) 
            {
                object value; 
                if (!values.TryGetValue(key2Value.Key, out value) || !object.Equals(key2Value.Value, value))
                {
                    return false;
                } 
                values.Remove(key2Value.Key);
            } 
            return true; 
        }
 
        #endregion


        ///  
        /// Returns an array of string/ pairs, one for each key value in this EntityKey,
        /// where the string is the key member name and the DbExpression is the value in this EntityKey 
        /// for that key member, represented as a  with the same result 
        /// type as the key member.
        ///  
        /// The command tree to use to create the key value constant expressions
        /// The entity set to which this EntityKey refers; used to verify that this key has the required key members
        /// The name -> expression mappings for the key member values represented by this EntityKey
        internal KeyValuePair[] GetKeyValueExpressions(DbCommandTree tree, EntitySet entitySet) 
        {
            Debug.Assert(!IsTemporary, "GetKeyValueExpressions doesn't make sense for temporary keys - they have no values."); 
            Debug.Assert(entitySet != null, "GetEntitySet should not return null."); 
            Debug.Assert(entitySet.Name == _entitySetName, "EntitySet returned from GetEntitySet has incorrect name.");
            int numKeyMembers = 0; 
            if (!IsTemporary)
            {
                if (_singletonKeyValue != null)
                { 
                    numKeyMembers = 1;
                } 
                else 
                {
                    numKeyMembers = _compositeKeyValues.Length; 
                }
            }
            if (((EntitySetBase)entitySet).ElementType.KeyMembers.Count != numKeyMembers)
            { 
                // If we found an entity set by name that's a different CLR reference
                // than the one contained by this EntityKey, the two entity sets could 
                // be incompatible.  The only error case we need to handle here is the 
                // one where the number of key members differs; other error cases
                // will be handled by the command tree builder methods. 

                //

 
                throw EntityUtil.EntitySetDoesNotMatch("metadataWorkspace", TypeHelpers.GetFullName(entitySet));
            } 
 
            // Iterate over the internal collection of string->object
            // key value pairs and create a list of string->constant 
            // expression key value pairs.
            KeyValuePair[] keyColumns;
            if (_singletonKeyValue != null)
            { 
                EdmMember singletonKeyMember = ((EntitySetBase)entitySet).ElementType.KeyMembers[0];
                Debug.Assert(singletonKeyMember != null, "Metadata for singleton key member shouldn't be null."); 
                keyColumns = new KeyValuePair[] { 
                    new KeyValuePair(singletonKeyMember.Name,
                        tree.CreateConstantExpression(_singletonKeyValue, Helper.GetModelTypeUsage(singletonKeyMember))) 
                };
            }
            else
            { 
                keyColumns = new KeyValuePair[_compositeKeyValues.Length];
                for (int i = 0; i < _compositeKeyValues.Length; ++i) 
                { 
                    Debug.Assert(_compositeKeyValues[i] != null, "Values within key-value pairs cannot be null.");
 
                    EdmMember keyMember = ((EntitySetBase)entitySet).ElementType.KeyMembers[i];
                    Debug.Assert(keyMember != null, "Metadata for key members shouldn't be null.");
                    keyColumns[i] = new KeyValuePair(keyMember.Name,
                        tree.CreateConstantExpression(_compositeKeyValues[i], Helper.GetModelTypeUsage(keyMember))); 
                }
            } 
 
            return keyColumns;
        } 

        /// 
        /// Returns a string representation of this EntityKey, for use in debugging.
        /// Note that the returned string contains potentially sensitive information 
        /// (i.e., key values), and thus shouldn't be publicly exposed.
        ///  
        internal string ConcatKeyValue() 
        {
            System.Text.StringBuilder builder = new System.Text.StringBuilder(); 
            builder.Append("EntitySet=").Append(_entitySetName);
            if (!IsTemporary)
            {
                foreach (EntityKeyMember pair in EntityKeyValues) 
                {
                    builder.Append(';'); 
                    builder.Append(pair.Key).Append("=").Append(pair.Value); 
                }
 
            }
            return builder.ToString();
        }
 
        /// 
        /// Returns the appropriate value for the given key name. 
        ///  
        internal object FindValueByName(string keyName)
        { 
            Debug.Assert(!IsTemporary, "FindValueByName should not be called for temporary keys.");
            if (SingletonKeyValue != null)
            {
                Debug.Assert(_keyNames[0] == keyName, "For a singleton key, the given keyName must match."); 
                return _singletonKeyValue;
            } 
            else 
            {
                object[] compositeKeyValues = CompositeKeyValues; 
                for (int i = 0; i < compositeKeyValues.Length; i++)
                {
                    if (keyName == _keyNames[i])
                    { 
                        return compositeKeyValues[i];
                    } 
                } 
                throw EntityUtil.ArgumentOutOfRange("keyName");
            } 
        }

        internal static void GetEntitySetName(string qualifiedEntitySetName, out  string entitySet, out string container)
        { 
            entitySet = null;
            container = null; 
            EntityUtil.CheckStringArgument(qualifiedEntitySetName, "qualifiedEntitySetName"); 

            string[] result = qualifiedEntitySetName.Split('.'); 
            if (result.Length != 2)
            {
                throw EntityUtil.InvalidQualifiedEntitySetName();
            } 

            container = result[0]; 
            entitySet = result[1]; 

            // both parts must be non-empty 
            if (container == null || container.Length == 0 ||
                entitySet == null || entitySet.Length == 0)
            {
                throw EntityUtil.InvalidQualifiedEntitySetName(); 
            }
 
            ValidateName(container); 
            ValidateName(entitySet);
        } 

        internal static void ValidateName(string name)
        {
            if (!System.Data.EntityModel.SchemaObjectModel.Utils.ValidUndottedName(name)) 
            {
                throw EntityUtil.EntityKeyInvalidName(name); 
            } 
        }
 
        #region Key Value Assignment and Validation

        private static void CheckKeyValues(EntitySet entitySet, IEnumerable> entityKeyValues,
            out string[] keyNames, out object singletonKeyValue, out object[] compositeKeyValues) 
        {
            Debug.Assert(entitySet != null, "EntitySet should not be null."); 
            EntityUtil.CheckArgumentNull(entityKeyValues, "entityKeyValues"); 

 
            int numExpectedKeyValues = 0;
            int numActualKeyValues = 0;
            ReadOnlyMetadataCollection keyMembers = null;
 
            singletonKeyValue = null;
            compositeKeyValues = null; 
 
            // Determine if we're a single or composite key.
            foreach (KeyValuePair value in entityKeyValues) 
            {
                numActualKeyValues++;
            }
 
            keyMembers = ((EntitySetBase)entitySet).ElementType.KeyMembers;
            keyNames = ((EntitySetBase)entitySet).ElementType.KeyMemberNames; 
            numExpectedKeyValues = keyMembers.Count; 
            Debug.Assert(numExpectedKeyValues > 0, "Metadata returned an invalid number of keys.");
            if (numExpectedKeyValues != numActualKeyValues) 
            {
                throw EntityUtil.IncorrectNumberOfKeyValuePairs("entityKeyValues", entitySet.ElementType.FullName, numExpectedKeyValues, numActualKeyValues);
            }
 
            if (numExpectedKeyValues == 1)
            { 
                foreach (KeyValuePair keyValuePair in entityKeyValues) 
                {
                    if (EntityUtil.IsNull(keyValuePair.Value) || String.IsNullOrEmpty(keyValuePair.Key)) 
                    {
                        throw EntityUtil.NoNullsAllowedInKeyValuePairs("entityKeyValues");
                    }
 
                    if (keyNames[0] != keyValuePair.Key)
                    {   // Validate key name 
                        Debug.Assert(null != entitySet, "null entityset"); 
                        throw EntityUtil.MissingKeyValue("entityKeyValues", keyNames[0], entitySet.ElementType.FullName);
                    } 
                    singletonKeyValue = keyValuePair.Value;

                    // Validate type of key value
                    Type entitySetKeyType = ((PrimitiveType)keyMembers[0].TypeUsage.EdmType).ClrEquivalentType; 
                    if (entitySetKeyType != singletonKeyValue.GetType())
                    { 
                        throw EntityUtil.IncorrectValueType("entityKeyValues", keyMembers[0].Name, entitySetKeyType.FullName, singletonKeyValue.GetType().FullName); 
                    }
                } 
            }
            else
            {
                compositeKeyValues = new object[numExpectedKeyValues]; 

                int i = 0; 
                for (i = 0; i < numExpectedKeyValues; ++i) 
                {
                    EdmMember keyField = ((EntitySetBase)entitySet).ElementType.KeyMembers[i]; 
                    bool foundMember = false;

                    foreach (KeyValuePair keyValuePair in entityKeyValues)
                    { 
                        if (keyField.Name == keyValuePair.Key)
                        { 
                            if (EntityUtil.IsNull(keyValuePair.Value) || String.IsNullOrEmpty(keyValuePair.Key)) 
                            {
                                throw EntityUtil.NoNullsAllowedInKeyValuePairs("entityKeyValues"); 
                            }
                            compositeKeyValues[i] = keyValuePair.Value;
                            foundMember = true;
 
                            // Validate type of key values
                            Type entitySetKeyType = ((PrimitiveType)keyField.TypeUsage.EdmType).ClrEquivalentType; 
                            if (entitySetKeyType != compositeKeyValues[i].GetType()) 
                            {
                                throw EntityUtil.IncorrectValueType("entityKeyValues", keyField.Name, entitySetKeyType.FullName, compositeKeyValues[i].GetType().FullName); 
                            }
                            break;
                        }
                    } 
                    // Validate Key Name (if we found it or not)
                    if (!foundMember) 
                    { 
                        throw EntityUtil.MissingKeyValue("entityKeyValues", keyField.Name, entitySet.ElementType.FullName);
                    } 
                }
            }
        }
 
        private static bool CheckKeyValues(IEnumerable> entityKeyValues,
            out string[] keyNames, out object singletonKeyValue, out object[] compositeKeyValues) 
        { 
            return CheckKeyValues(entityKeyValues, false, false, out keyNames, out singletonKeyValue, out compositeKeyValues);
        } 

        private static bool CheckKeyValues(IEnumerable> entityKeyValues, bool allowNullKeys, bool tokenizeStrings,
            out string[] keyNames, out object singletonKeyValue, out object[] compositeKeyValues)
        { 
            EntityUtil.CheckArgumentNull(entityKeyValues, "entityKeyValues");
 
            int numExpectedKeyValues; 
            int numActualKeyValues = 0;
 
            keyNames = null;
            singletonKeyValue = null;
            compositeKeyValues = null;
 
            // Determine if we're a single or composite key.
            foreach (KeyValuePair value in entityKeyValues) 
            { 
                numActualKeyValues++;
            } 

            numExpectedKeyValues = numActualKeyValues;
            if (numExpectedKeyValues == 0)
            { 
                if (!allowNullKeys)
                { 
                    throw EntityUtil.EntityKeyMustHaveValues("entityKeyValues"); 
                }
            } 
            else
            {
                keyNames = new string[numExpectedKeyValues];
 
                if (numExpectedKeyValues == 1)
                { 
                    lock (_nameLookup) 
                    {
                        foreach (KeyValuePair keyValuePair in entityKeyValues) 
                        {
                            if (EntityUtil.IsNull(keyValuePair.Value) || String.IsNullOrEmpty(keyValuePair.Key))
                            {
                                throw EntityUtil.NoNullsAllowedInKeyValuePairs("entityKeyValues"); 
                            }
                            ValidateName(keyValuePair.Key); 
                            keyNames[0] = tokenizeStrings ? EntityKey.LookupSingletonName(keyValuePair.Key) : keyValuePair.Key; 
                            singletonKeyValue = keyValuePair.Value;
                        } 
                    }
                }
                else
                { 
                    compositeKeyValues = new object[numExpectedKeyValues];
 
                    int i = 0; 
                    lock (_nameLookup)
                    { 
                        foreach (KeyValuePair keyValuePair in entityKeyValues)
                        {
                            if (EntityUtil.IsNull(keyValuePair.Value) || String.IsNullOrEmpty(keyValuePair.Key))
                            { 
                                throw EntityUtil.NoNullsAllowedInKeyValuePairs("entityKeyValues");
                            } 
                            Debug.Assert(null == keyNames[i], "shouldn't have a name yet"); 
                            ValidateName(keyValuePair.Key);
                            keyNames[i] = tokenizeStrings ? EntityKey.LookupSingletonName(keyValuePair.Key) : keyValuePair.Key; 
                            compositeKeyValues[i] = keyValuePair.Value;
                            i++;
                        }
                    } 
                }
            } 
            return numExpectedKeyValues > 0; 
        }
 
        /// 
        /// Validates the record parameter passed to the EntityKey constructor,
        /// and converts the data into the form required by EntityKey.  For singleton keys,
        /// this is a single object.  For composite keys, this is an object array. 
        /// 
        /// the entity set metadata object which this key refers to 
        /// the parameter to validate 
        /// the number of expected key-value pairs
        /// the name of the argument to use in exception messages 
        /// the validated value(s) (for a composite key, an object array is returned)
        private static void CheckKeyValues(EntitySet entitySet, IExtendedDataRecord record,
            out string[] keyNames, out object singletonKeyValue, out object[] compositeKeyValues)
        { 
            singletonKeyValue = null;
            compositeKeyValues = null; 
 
            int numExpectedKeyValues = ((EntitySetBase)entitySet).ElementType.KeyMembers.Count;
            keyNames = ((EntitySetBase)entitySet).ElementType.KeyMemberNames; 

            EntityType entityType = record.DataRecordInfo.RecordType.EdmType as EntityType;
            if (entityType == null)
            { 
                throw EntityUtil.DataRecordMustBeEntity();
            } 
 
            // ensure the type contained by this entity set matches the type contained by the data record
            if (entitySet != null && !entitySet.ElementType.IsAssignableFrom(entityType)) 
            {
                throw EntityUtil.EntityTypesDoNotMatch(entityType.Name, entitySet.ElementType.FullName);
            }
            Debug.Assert(numExpectedKeyValues > 0, "Should be expecting a positive number of key-values."); 

            if (numExpectedKeyValues == 1) 
            { 
                // Optimize for a singleton key.
 
                EdmMember member = entityType.KeyMembers[0];
                try
                {
                    singletonKeyValue = CheckValue("record", member.Name, record[member.Name], (PrimitiveType)member.TypeUsage.EdmType); 
                }
                catch (IndexOutOfRangeException ex) 
                { 
                    throw EntityUtil.MissingKeyValue("record", member.Name, entityType.FullName, ex);
                } 
            }
            else
            {
                compositeKeyValues = new object[numExpectedKeyValues]; 
                // grab each key-field from the data record
                for (int i = 0; i < numExpectedKeyValues; ++i) 
                { 
                    EdmMember member = entityType.KeyMembers[i];
                    try 
                    {
                        compositeKeyValues[i] = CheckValue("record", member.Name, record[member.Name],
                            (PrimitiveType)member.TypeUsage.EdmType);
                    } 
                    catch (IndexOutOfRangeException ex)
                    { 
                        throw EntityUtil.MissingKeyValue("record", member.Name, entityType.FullName, ex); 
                    }
                } 
            }
        }

        ///  
        /// Checks whether the given value is valid for the given key field.
        ///  
        /// the name of the argument that contains a value of the given CLR type 
        /// the name of the key field that contains the value of the given CLR type
        /// the value to validate 
        /// the expected type of the value
        /// the validated value
        /// the value is null
        /// the value is of an incorrect type 
        private static object CheckValue(string argumentName, string keyFieldName, object value, PrimitiveType expectedType)
        { 
            Debug.Assert(expectedType != null, "expectedType cannot be null"); 
            if (EntityUtil.IsNull(value))
            { 
                throw EntityUtil.NoNullsAllowedInKeyValuePairs(argumentName);
            }
            if (expectedType.ClrEquivalentType != value.GetType())
            { 
                throw EntityUtil.IncorrectValueType(argumentName, keyFieldName, expectedType.ClrEquivalentType.FullName, value.GetType().FullName);
            } 
            return value; 
        }
        ///  
        /// Verify that the types of the objects passed in to be used as keys actually match the types from the model.
        /// This error is also caught when the entity is materialized and when the key value is set, at which time it
        /// also throws ThrowSetInvalidValue().
        /// SQLBUDT 513838. This error is possible and should be caught at run time, not in an assertion. 
        /// 
        /// The EntitySet to validate against 
        internal void ValidateEntityKey(EntitySet entitySet) 
        {
            ValidateEntityKey(entitySet, false, null); 
        }
        /// 
        /// Verify that the types of the objects passed in to be used as keys actually match the types from the model.
        /// This error is also caught when the entity is materialized and when the key value is set, at which time it 
        /// also throws ThrowSetInvalidValue().
        /// SQLBUDT 513838. This error is possible and should be caught at run time, not in an assertion. 
        ///  
        /// The EntitySet to validate against
        /// Type of the exception to throw 
        internal void ValidateEntityKey(EntitySet entitySet, bool isArgumentException, string argumentName)
        {
            if (entitySet != null)
            { 
                ReadOnlyMetadataCollection keyMembers = ((EntitySetBase)entitySet).ElementType.KeyMembers;
                if (_singletonKeyValue != null) 
                { 
                    // 1. Validate number of keys
                    if (keyMembers.Count != 1) 
                    {
                        if (isArgumentException)
                        {
                            throw EntityUtil.IncorrectNumberOfKeyValuePairs(argumentName, entitySet.ElementType.FullName, keyMembers.Count, 1); 
                        }
                        else 
                        { 
                            throw EntityUtil.IncorrectNumberOfKeyValuePairsInvalidOperation(entitySet.ElementType.FullName, keyMembers.Count, 1);
                        } 
                    }

                    // 2. Validate type of key values
                    Type entitySetKeyType = ((PrimitiveType)keyMembers[0].TypeUsage.EdmType).ClrEquivalentType; 
                    if (entitySetKeyType != _singletonKeyValue.GetType())
                    { 
                        if (isArgumentException) 
                        {
                            throw EntityUtil.IncorrectValueType(argumentName, keyMembers[0].Name, entitySetKeyType.FullName, _singletonKeyValue.GetType().FullName); 
                        }
                        else
                        {
                            throw EntityUtil.IncorrectValueTypeInvalidOperation(keyMembers[0].Name, entitySetKeyType.FullName, _singletonKeyValue.GetType().FullName); 
                        }
                    } 
 
                    // 3. Validate key names
                    if (_keyNames[0] != keyMembers[0].Name) 
                    {
                        if (isArgumentException)
                        {
                            throw EntityUtil.MissingKeyValue(argumentName, keyMembers[0].Name, entitySet.ElementType.FullName); 
                        }
                        else 
                        { 
                            throw EntityUtil.MissingKeyValueInvalidOperation(keyMembers[0].Name, entitySet.ElementType.FullName);
                        } 
                    }
                }
                else if (null != _compositeKeyValues)
                { 
                    // 1. Validate number of keys
                    if (keyMembers.Count != _compositeKeyValues.Length) 
                    { 
                        if (isArgumentException)
                        { 
                            throw EntityUtil.IncorrectNumberOfKeyValuePairs(argumentName, entitySet.ElementType.FullName, keyMembers.Count, _compositeKeyValues.Length);
                        }
                        else
                        { 
                            throw EntityUtil.IncorrectNumberOfKeyValuePairsInvalidOperation(entitySet.ElementType.FullName, keyMembers.Count, _compositeKeyValues.Length);
                        } 
                    } 

                    for (int i = 0; i < _compositeKeyValues.Length; ++i) 
                    {
                        EdmMember keyField = ((EntitySetBase)entitySet).ElementType.KeyMembers[i];
                        bool foundMember = false;
                        for(int j = 0; j < _compositeKeyValues.Length; ++j) 
                        {
                            if (keyField.Name == _keyNames[j]) 
                            { 
                                // 2. Validate type of key values
                                Type entitySetKeyType = ((PrimitiveType)keyField.TypeUsage.EdmType).ClrEquivalentType; 
                                if (entitySetKeyType != _compositeKeyValues[j].GetType())
                                {
                                    if (isArgumentException)
                                    { 
                                        throw EntityUtil.IncorrectValueType(argumentName, keyField.Name, entitySetKeyType.FullName, _compositeKeyValues[j].GetType().FullName);
                                    } 
                                    else 
                                    {
                                        throw EntityUtil.IncorrectValueTypeInvalidOperation(keyField.Name, entitySetKeyType.FullName, _compositeKeyValues[j].GetType().FullName); 
                                    }
                                }
                                foundMember = true;
                                break; 
                            }
                        } 
                        // 3. Validate Key Name (if we found it or not) 
                        if (!foundMember)
                        { 
                            if (isArgumentException)
                            {
                                throw EntityUtil.MissingKeyValue(argumentName, keyField.Name, entitySet.ElementType.FullName);
                            } 
                            else
                            { 
                                throw EntityUtil.MissingKeyValueInvalidOperation(keyField.Name, entitySet.ElementType.FullName); 
                            }
                        } 
                    }
                }
            }
        } 

        ///  
        /// Asserts that the "state" of the EntityKey is correct, by validating assumptions 
        /// based on whether the key is a singleton, composite, or temporary.
        ///  
        /// whether we expect this EntityKey to be marked temporary
        [Conditional("DEBUG")]
        private void AssertCorrectState(EntitySetBase entitySet, bool isTemporary)
        { 
            if (_singletonKeyValue != null)
            { 
                Debug.Assert(!isTemporary, "Singleton keys should not be expected to be temporary."); 
                Debug.Assert(_compositeKeyValues == null, "The EntityKey is marked as both a singleton key and a composite key - this is illegal.");
                if (entitySet != null) 
                {
                    Debug.Assert(entitySet.ElementType.KeyMembers.Count == 1, "For a singleton key, the number of key fields must be exactly 1.");
                }
            } 
            else if (_compositeKeyValues != null)
            { 
                Debug.Assert(!isTemporary, "Composite keys should not be expected to be temporary."); 
                if (entitySet != null)
                { 
                    Debug.Assert(entitySet.ElementType.KeyMembers.Count > 1, "For a compsite key, the number of key fields should be greater than 1.");
                    Debug.Assert(entitySet.ElementType.KeyMembers.Count == _compositeKeyValues.Length, "Incorrect number of values specified to composite key.");
                }
                for (int i = 0; i < _compositeKeyValues.Length; ++i) 
                {
                    Debug.Assert(_compositeKeyValues[i] != null, "Values passed to a composite EntityKey cannot be null."); 
                } 
            }
            else if (!IsTemporary) 
            {
                // one of our static keys
                Debug.Assert(!isTemporary, "Static keys should not be expected to be temporary.");
                Debug.Assert(this.EntityKeyValues == null, "The EntityKeyValues property for Static EntityKeys must return null."); 
                Debug.Assert(this.EntityContainerName == null, "The EntityContainerName property for Static EntityKeys must return null.");
                Debug.Assert(this.EntitySetName != null, "The EntitySetName property for Static EntityKeys must not return null."); 
            } 
            else
            { 
                Debug.Assert(isTemporary, "The EntityKey is marked as neither a singleton or composite.  Therefore, it should be expected to be temporary.");
                Debug.Assert(this.IsTemporary, "The EntityKey is marked as neither a singleton or composite.  Therefore it must be marked as temporary.");
                Debug.Assert(this.EntityKeyValues == null, "The EntityKeyValues property for temporary EntityKeys must return null.");
            } 
        }
 
        #endregion 

        #region Serialization 

        /// 
        ///
        ///  
        /// 
        [OnDeserializing] 
        public void OnDeserializing(StreamingContext context) 
        {
            if (RequiresDeserialization) 
            {
                DeserializeMembers();
            }
        } 

        ///  
        /// 
        /// 
        ///  
        [OnDeserialized]
        public void OnDeserialized(StreamingContext context)
        {
            lock (_nameLookup) 
            {
                _entitySetName = LookupSingletonName(_entitySetName); 
                _entityContainerName = LookupSingletonName(_entityContainerName); 
                if (_keyNames != null)
                { 
                    for (int i = 0; i < _keyNames.Length; i++)
                    {
                        _keyNames[i] = LookupSingletonName(_keyNames[i]);
                    } 
                }
            } 
        } 

        ///  
        /// Dev Note: this must be called from within a _lock block on _nameLookup
        /// 
        /// 
        ///  
        private static string LookupSingletonName(string name)
        { 
            if (String.IsNullOrEmpty(name)) 
            {
                return null; 
            }
            if (_nameLookup.ContainsKey(name))
            {
                return _nameLookup[name]; 
            }
            _nameLookup.Add(name, name); 
            return name; 
        }
 
        private void ValidateWritable(object instance)
        {
            if (_isLocked || instance != null)
            { 
                throw EntityUtil.CannotChangeEntityKey();
            } 
        } 

        private bool RequiresDeserialization 
        {
            get { return _deserializedMembers != null; }
        }
 
        private void DeserializeMembers()
        { 
            if (CheckKeyValues(new KeyValueReader(_deserializedMembers), true, true, out _keyNames, out _singletonKeyValue, out _compositeKeyValues)) 
            {
                // If we received values from the _deserializedMembers, then we do not need to track these any more 
                _deserializedMembers = null;
            }
        }
 
        #endregion
 
        private class KeyValueReader : IEnumerable> 
        {
            IEnumerable _enumerator; 

            public KeyValueReader(IEnumerable enumerator)
            {
                _enumerator = enumerator; 
            }
 
            #region IEnumerable> Members 

            public IEnumerator> GetEnumerator() 
            {
                foreach (EntityKeyMember pair in _enumerator)
                {
                    if (pair != null) 
                    {
                        yield return new KeyValuePair(pair.Key, pair.Value); 
                    } 
                }
            } 

            #endregion

            #region IEnumerable Members 

            IEnumerator IEnumerable.GetEnumerator() 
            { 
                return this.GetEnumerator();
            } 

            #endregion
        }
    } 

    ///  
    /// Information about a key that is part of an EntityKey. 
    /// A key member contains the key name and value.
    ///  
    [DataContract]
    [Serializable]
    public class EntityKeyMember
    { 
        private string _keyName;
        private object _keyValue; 
 
        /// 
        /// Creates an empty EntityKeyMember. This constructor is used by serialization. 
        /// 
        public EntityKeyMember()
        {
        } 

        ///  
        /// Creates a new EntityKeyMember with the specified key name and value. 
        /// 
        /// The key name 
        /// The key value
        public EntityKeyMember(string keyName, object keyValue)
        {
            EntityUtil.CheckArgumentNull(keyName, "keyName"); 
            EntityUtil.CheckArgumentNull(keyValue, "keyValue");
            _keyName = keyName; 
            _keyValue = keyValue; 
        }
 
        /// 
        /// The key name
        /// 
        [DataMember] 
        public string Key
        { 
            get 
            {
                return _keyName; 
            }
            set
            {
                ValidateWritable(_keyName); 
                EntityUtil.CheckArgumentNull(value, "value");
                _keyName = value; 
            } 
        }
 
        /// 
        /// The key value
        /// 
        [DataMember] 
        public object Value
        { 
            get 
            {
                return _keyValue; 
            }
            set
            {
                ValidateWritable(_keyValue); 
                EntityUtil.CheckArgumentNull(value, "value");
                _keyValue = value; 
            } 
        }
 
        /// 
        /// Returns a string representation of the EntityKeyMember
        /// 
        /// A string representation of the EntityKeyMember 
        public override string ToString()
        { 
            return String.Format(System.Globalization.CultureInfo.CurrentCulture, "[{0}, {1}]", _keyName, _keyValue); 
        }
 
        /// 
        /// Ensures that the instance can be written to (value must be null)
        /// 
        private void ValidateWritable(object instance) 
        {
            if (instance != null) 
            { 
                throw EntityUtil.CannotChangeEntityKey();
            } 
        }
    }
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System.Collections.Generic;
using System.Collections.ObjectModel; 
using System.Data;
using System.Diagnostics;
using System.Collections;
using System.Data.Common; 
using System.Data.Common.Utils;
using System.Data.Metadata.Edm; 
using System.Data.Mapping; 
using System.Data.Objects;
using System.Data.Common.CommandTrees; 
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.Xml;
 
namespace System.Data
{ 
    ///  
    /// An identifier for an entity.
    ///  
    [DebuggerDisplay("{ConcatKeyValue()}")]
    [Serializable]
    [DataContract(IsReference=true)]
    public sealed class EntityKey : IEquatable 
    {
        // The implementation of EntityKey is optimized for the following common cases: 
        //      1) Keys constructed internally rather by the user - in particular, keys 
        //         created by the bridge on the round-trip from query.
        //      2) Single-valued (as opposed to composite) keys. 
        // We accomplish this by maintaining two variables, at most one of which is non-null.
        // The first is of type object and in the case of a singleton key, is set to the
        // single key value.  The second is an object array and in the case of
        // a composite key, is set to the list of key values.  If both variables are null, 
        // the EntityKey is a temporary key.  Note that the key field names
        // are not stored - for composite keys, the values are stored in the order in which 
        // metadata reports the corresponding key members. 

        // The following 5 fields are serialized.  Adding or removing a serialized field is considered 
        // a breaking change.  This includes changing the field type or field name of existing
        // serialized fields. If you need to make this kind of change, it may be possible, but it
        // will require some custom serialization/deserialization code.
        private string _entitySetName; 
        private string _entityContainerName;
        private object _singletonKeyValue;      // non-null for singleton keys 
        private object[] _compositeKeyValues;   // non-null for composite keys 
        private string[] _keyNames;             // key names that correspond to the key values
        private bool _isLocked;                 // determines if this key is lock from writing 

        [NonSerialized]
        private EntityKeyMember[] _deserializedMembers;
 
        // The hash code is not serialized since it can be computed differently on the deserialized system.
        [NonSerialized] 
        private int _hashCode;                  // computed as needed 

 
        // Names for constant EntityKeys
        private const string s_NoEntitySetKey = "NoEntitySetKey.NoEntitySetKey";
        private const string s_EntityNotValidKey = "EntityNotValidKey.EntityNotValidKey";
 
        /// 
        /// A singleton EntityKey by which a readonly entity is identified. 
        ///  
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]     // Justification: these are internal so they cannot be modified publically
        public static readonly EntityKey NoEntitySetKey = new EntityKey(s_NoEntitySetKey); 

        /// 
        /// A singleton EntityKey identifying an entity resulted from a failed TREAT.
        ///  
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]     // Justification: these are internal so they cannot be modified publically
        public static readonly EntityKey EntityNotValidKey = new EntityKey(s_EntityNotValidKey); 
 
        /// 
        /// A dictionary of names so that singleton instances of names can be used 
        /// 
        private static Dictionary _nameLookup = new Dictionary();

        #region Public Constructors 

        ///  
        /// Constructs an empty EntityKey. For use during XmlSerialization. 
        /// 
        public EntityKey() 
        {
            _isLocked = false;
        }
 
        /// 
        /// Constructs an EntityKey with the given key values. 
        ///  
        /// The EntitySet name, qualified by the EntityContainer name, of the entity
        /// The key-value pairs that identify the entity 
        public EntityKey(string qualifiedEntitySetName, IEnumerable> entityKeyValues)
        {
            GetEntitySetName(qualifiedEntitySetName, out _entitySetName, out _entityContainerName);
            CheckKeyValues(entityKeyValues, out _keyNames, out _singletonKeyValue, out _compositeKeyValues); 
            AssertCorrectState(null, false);
            _isLocked = true; 
        } 

        ///  
        /// Constructs an EntityKey with the given key values.
        /// 
        /// The EntitySet name, qualified by the EntityContainer name, of the entity
        /// The key-value pairs that identify the entity 
        public EntityKey(string qualifiedEntitySetName, IEnumerable entityKeyValues)
        { 
            GetEntitySetName(qualifiedEntitySetName, out _entitySetName, out _entityContainerName); 
            CheckKeyValues(new KeyValueReader(entityKeyValues), out _keyNames, out _singletonKeyValue, out _compositeKeyValues);
            AssertCorrectState(null, false); 
            _isLocked = true;
        }

        ///  
        /// Constructs an EntityKey with the given single key name and value.
        ///  
        /// The EntitySet name, qualified by the EntityContainer name, of the entity 
        /// The key name that identifies the entity
        /// The key value that identifies the entity 
        public EntityKey(string qualifiedEntitySetName, string keyName, object keyValue)
        {
            GetEntitySetName(qualifiedEntitySetName, out _entitySetName, out _entityContainerName);
            EntityUtil.CheckStringArgument(keyName, "keyName"); 
            EntityUtil.CheckArgumentNull(keyValue, "keyValue");
 
            _keyNames = new string[1]; 
            ValidateName(keyName);
            _keyNames[0] = keyName; 
            _singletonKeyValue = keyValue;

            AssertCorrectState(null, false);
            _isLocked = true; 
        }
 
        #endregion 

        #region Internal Constructors 

        /// 
        /// Constructs an EntityKey from an IExtendedDataRecord representing the entity.
        ///  
        /// EntitySet of the entity
        /// an IExtendedDataRecord that represents the entity 
        internal EntityKey(EntitySet entitySet, IExtendedDataRecord record) 
        {
            EntityUtil.CheckArgumentNull(entitySet, "entitySet"); 
            EntityUtil.CheckArgumentNull(entitySet.Name, "entitySet.Name");
            EntityUtil.CheckArgumentNull(entitySet.EntityContainer, "entitySet.Container");
            EntityUtil.CheckArgumentNull(entitySet.EntityContainer.Name, "entitySet.Container.Name");
            EntityUtil.CheckArgumentNull(record, "record"); 
            _entitySetName = entitySet.Name;
            _entityContainerName = entitySet.EntityContainer.Name; 
 
            CheckKeyValues(entitySet, record, out _keyNames, out _singletonKeyValue, out _compositeKeyValues);
            AssertCorrectState(entitySet, false); 
            _isLocked = true;
        }

        ///  
        /// Constructs an EntityKey from an IExtendedDataRecord representing the entity.
        ///  
        /// EntitySet of the entity 
        /// an IExtendedDataRecord that represents the entity
        internal EntityKey(string qualifiedEntitySetName) 
        {
            GetEntitySetName(qualifiedEntitySetName, out _entitySetName, out _entityContainerName);
            _isLocked = true;
        } 

        ///  
        /// Constructs a temporary EntityKey with the given EntitySet. 
        /// Temporary keys do not store key field names
        ///  
        /// EntitySet of the entity
        internal EntityKey(EntitySetBase entitySet)
        {
            EntityUtil.CheckArgumentNull(entitySet, "entitySet"); 
            Debug.Assert(entitySet.EntityContainer != null, "EntitySet.EntityContainer cannot be null.");
 
            _entitySetName = entitySet.Name; 
            _entityContainerName = entitySet.EntityContainer.Name;
 
            AssertCorrectState(entitySet, true);
            _isLocked = true;
        }
 
        /// 
        /// Constructor optimized for a singleton key. 
        /// SQLBUDT 478655: Performance optimization: Does no integrity checking on the key value. 
        /// SQLBUDT 523554: Performance optimization: Does no validate type of key members.
        ///  
        /// EntitySet of the entity
        /// The single value that composes the entity's key, assumed to contain the correct type.
        internal EntityKey(EntitySetBase entitySet, object singletonKeyValue)
        { 
            Debug.Assert(entitySet != null, "EntitySet cannot be null.");
            Debug.Assert(entitySet.EntityContainer != null, "EntitySet.EntityContainer cannot be null."); 
            Debug.Assert(singletonKeyValue != null, "Singleton key value cannot be null."); 
            _singletonKeyValue = singletonKeyValue;
            _entitySetName = entitySet.Name; 
            _entityContainerName = entitySet.EntityContainer.Name;
            _keyNames = entitySet.ElementType.KeyMemberNames; // using EntitySetBase avoids an (EntityType) cast that EntitySet encoure
            AssertCorrectState(entitySet, false);
            _isLocked = true; 
        }
 
        ///  
        /// Constructor optimized for a composite key.
        /// SQLBUDT 478655: Performance optimization: Does no integrity checking on the key values. 
        /// SQLBUDT 523554: Performance optimization: Does no validate type of key members.
        /// 
        /// EntitySet of the entity
        /// A list of the values (at least 2) that compose the entity's key, assumed to contain correct types. 
        internal EntityKey(EntitySetBase entitySet, object[] compositeKeyValues)
        { 
            Debug.Assert(entitySet != null, "EntitySet cannot be null."); 
            Debug.Assert(entitySet.EntityContainer != null, "EntitySet.EntityContainer cannot be null.");
            Debug.Assert(compositeKeyValues != null, "Composite key values cannot be null."); 
            _compositeKeyValues = compositeKeyValues;
            _entitySetName = entitySet.Name;
            _entityContainerName = entitySet.EntityContainer.Name;
            _keyNames = entitySet.ElementType.KeyMemberNames; // using EntitySetBase avoids an (EntityType) cast that EntitySet encoure 
            AssertCorrectState(entitySet, false);
            _isLocked = true; 
        } 

        #endregion 

        /// 
        /// Gets the EntitySet name identifying the entity set that contains the entity.
        ///  
        [DataMember]
        public string EntitySetName 
        { 
            get
            { 
                return _entitySetName;
            }
            set
            { 
                ValidateWritable(_entitySetName);
                lock(_nameLookup) 
                { 
                    _entitySetName = EntityKey.LookupSingletonName(value);
                } 
            }
        }

        ///  
        /// Gets the EntityContainer name identifying the entity container that contains the entity.
        ///  
        [DataMember] 
        public string EntityContainerName
        { 
            get
            {
                return _entityContainerName;
            } 
            set
            { 
                ValidateWritable(_entityContainerName); 
                lock (_nameLookup)
                { 
                    _entityContainerName = EntityKey.LookupSingletonName(value);
                }
            }
        } 

 
        ///  
        /// Gets the key values that identify the entity.
        ///  
        [DataMember]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification="Required for this feature")]
        public EntityKeyMember[] EntityKeyValues
        { 
            get
            { 
                if(!IsTemporary) 
                {
                    EntityKeyMember[] keyValues; 
                    if (_singletonKeyValue != null)
                    {
                        keyValues = new EntityKeyMember[] {
                                new EntityKeyMember(_keyNames[0], _singletonKeyValue) }; 
                    }
                    else 
                    { 
                        keyValues = new EntityKeyMember[_compositeKeyValues.Length];
                        for (int i = 0; i < _compositeKeyValues.Length; ++i) 
                        {
                            keyValues[i] = new EntityKeyMember(_keyNames[i], _compositeKeyValues[i]);
                        }
                    } 
                    return keyValues;
                } 
                else 
                {
                    return null; 
                }
            }
            set
            { 
                ValidateWritable(_keyNames);
                if (value != null) 
                { 
                    if (!CheckKeyValues(new KeyValueReader(value), true, true, out _keyNames, out _singletonKeyValue, out _compositeKeyValues))
                    { 
                        // If we did not retrieve values from the setter (i.e. encoded settings), we need to keep track of the
                        // array instance because the array members will be set next.
                        _deserializedMembers = value;
                    } 
                }
            } 
        } 

        ///  
        /// Gets a value indicating whether this key is a temporary key.
        /// 
        public bool IsTemporary
        { 
            get
            { 
                return (SingletonKeyValue == null) && (CompositeKeyValues == null); 
            }
        } 

        private object SingletonKeyValue
        {
            get 
            {
                if (RequiresDeserialization) 
                { 
                    DeserializeMembers();
                } 
                return _singletonKeyValue;
            }
        }
 
        private object[] CompositeKeyValues
        { 
            get 
            {
                if (RequiresDeserialization) 
                {
                    DeserializeMembers();
                }
                return _compositeKeyValues; 
            }
        } 
 
        /// 
        /// Gets the entity set for this entity key from the given metadata workspace, by 
        /// entity container name and entity set name.
        /// 
        /// workspace in which to look up the entity set
        /// the entity set from the given workspace for this entity key 
        /// the entity set could not be located in the workspace
        public EntitySet GetEntitySet(MetadataWorkspace metadataWorkspace) 
        { 
            EntityUtil.CheckArgumentNull(metadataWorkspace, "metadataWorkspace");
            if (String.IsNullOrEmpty(_entityContainerName) || String.IsNullOrEmpty(_entitySetName)) 
            {
                throw EntityUtil.MissingQualifiedEntitySetName();
            }
 
            // GetEntityContainer will throw if it cannot find the container
 
            // SQLBUDT 479443:  If this entity key was initially created using an entity set 
            // from a different workspace, look up the entity set in the new workspace.
            // Metadata will throw an ArgumentException if the entity set could not be found. 

            return metadataWorkspace
                .GetEntityContainer(_entityContainerName, DataSpace.CSpace)
                .GetEntitySetByName(_entitySetName, false); 
        }
 
        #region Equality/Hashing 

        ///  
        /// Compares this instance to a given key by their values.
        /// 
        /// the key to compare against this instance
        /// true if this instance is equal to the given key, and false otherwise 
        public override bool Equals(object obj)
        { 
            return InternalEquals(this, obj as EntityKey); 
        }
 
        /// 
        /// Compares this instance to a given key by their values.
        /// 
        /// the key to compare against this instance 
        /// true if this instance is equal to the given key, and false otherwise
        public bool Equals(EntityKey other) 
        { 
            return InternalEquals(this, other);
        } 

        /// 
        /// Returns a value-based hash code, to allow EntityKey to be used in hash tables.
        ///  
        /// the hash value of this EntityKey
        public override int GetHashCode() 
        { 
            int hashCode = _hashCode;
            if (0 == hashCode) 
            {
                if (RequiresDeserialization)
                {
                    DeserializeMembers(); 
                }
 
                int baseHash = 0; 
                if (_entitySetName != null)
                { 
                    baseHash += _entitySetName.GetHashCode();
                }
                if (_entityContainerName != null)
                { 
                    baseHash += _entityContainerName.GetHashCode();
                } 
 
                // If the key is not temporary, determine a hash code based on the value(s) within the key.
                if (null != _singletonKeyValue) 
                {
                    hashCode = unchecked(baseHash ^ _singletonKeyValue.GetHashCode());
                }
                else if (null != _compositeKeyValues) 
                {
                    hashCode = unchecked(baseHash ^ GetHashCode(_compositeKeyValues)); 
                } 
                else
                { 
                    // If the key is temporary, use default hash code
                    hashCode = base.GetHashCode();
                }
 
                // cache the hash code if we are a locked or fully specified EntityKey
                if (_isLocked || (!String.IsNullOrEmpty(_entitySetName) && 
                                  !String.IsNullOrEmpty(_entityContainerName) && 
                                  (_singletonKeyValue != null || _compositeKeyValues != null)))
                { 
                    _hashCode = hashCode;
                }
            }
            return hashCode; 
        }
 
        private static int GetHashCode(object[] compositeKeyValues) 
        {
            int hashCode = 0; 
            for(int i = 0; i < compositeKeyValues.Length; ++i)
            {
                // For a composite key, chain-XOR each value to compute the hash code.
                // This implies that the keys for the following two objects (from the 
                // same entity set) will hash to the same value, despite being unequal.
                // This isn't a bug, just a known hash collision. 
                //      Object1: 
                //          KeyField1 = 10;
                //          KeyField2 = 20; 
                //      Object2:
                //          KeyField1 = 20;
                //          KeyField2 = 10;
                unchecked { 
                    hashCode += compositeKeyValues[i].GetHashCode();
                } 
            } 
            return hashCode;
        } 

        /// 
        /// Compares two keys by their values.
        ///  
        /// a key to compare
        /// a key to compare 
        /// true if the two keys are equal, false otherwise 
        public static bool operator ==(EntityKey key1, EntityKey key2)
        { 
#if DEBUG
            if (((object)NoEntitySetKey == (object)key1) || ((object)EntityNotValidKey == (object)key1) ||
                ((object)NoEntitySetKey == (object)key2) || ((object)EntityNotValidKey == (object)key1)
                // || (null==(object)key1) || (null==(object)key2)) //To check for internal use of null==key 
                )
            { 
                Debug.Assert(typeof(EntityKey).Assembly != System.Reflection.Assembly.GetCallingAssembly(), "When comparing an EntityKey to one of the predefined types (EntityKey.NoEntitySetKey or EntityKey.EntityNotValidKey), use Object.ReferenceEquals()"); 
            }
#endif 
            return InternalEquals(key1, key2);
        }

        ///  
        /// Compares two keys by their values.
        ///  
        /// a key to compare 
        /// a key to compare
        /// true if the two keys are not equal, false otherwise 
        public static bool operator !=(EntityKey key1, EntityKey key2)
        {
#if DEBUG
            if (((object)NoEntitySetKey == (object)key1) || ((object)EntityNotValidKey == (object)key1) || 
                ((object)NoEntitySetKey == (object)key2) || ((object)EntityNotValidKey == (object)key1))
            // || (null==(object)key1) || (null==(object)key2)) //To check for internal use of null==key 
            { 
                Debug.Assert(typeof(EntityKey).Assembly != System.Reflection.Assembly.GetCallingAssembly(), "When comparing an EntityKey to one of the predefined types (EntityKey.NoEntitySetKey or EntityKey.EntityNotValidKey), use Object.ReferenceEquals()");
            } 
#endif
            return !InternalEquals(key1, key2);
        }
 
        /// 
        /// Internal function to compare two keys by their values. 
        ///  
        /// a key to compare
        /// a key to compare 
        /// true if the two keys are equal, false otherwise
        private static bool InternalEquals(EntityKey key1, EntityKey key2)
        {
            // If both are null or refer to the same object, they're equal. 
            if (object.ReferenceEquals(key1, key2))
            { 
                return true; 
            }
 
            // If exactly one is null (avoid calling EntityKey == operator overload), they're not equal.
            if (object.ReferenceEquals(key1, null) || object.ReferenceEquals(key2, null))
            {
                return false; 
            }
 
            if (key1.RequiresDeserialization) 
            {
                key1.DeserializeMembers(); 
            }
            else if (key2.RequiresDeserialization)
            {
                key2.DeserializeMembers(); 
            }
 
            // If the hash codes differ, the keys are not equal.  Note that 
            // a key's hash code is cached after being computed for the first time,
            // so this check will only incur the cost of computing a hash code 
            // at most once for a given key.

            // The primary caller is Dictionary
            // at which point Equals is only called after HashCode was deteremined to be equal 
            //if (key1.GetHashCode() != key2.GetHashCode())
            //{ 
            //    return false; 
            //}
 
            if (null != key1._singletonKeyValue)
            {
                // Compare the single value (if the second is null, false should be returned)
                return (key1._singletonKeyValue.Equals(key2._singletonKeyValue) && 
                        String.Equals(key1._keyNames[0], key2._keyNames[0]) &&
                        String.Equals(key1._entityContainerName, key2._entityContainerName) && 
                        String.Equals(key1._entitySetName, key2._entitySetName)); 
            }
 
            // If either key is temporary, they're not equal.  This is because
            // temporary keys are compared by CLR reference, and we've already
            // checked reference equality.
            // If the first key is a composite key and the second one isn't, they're not equal. 

            // They're both composite keys, so compare each value. 
            return ((null != key1._compositeKeyValues) && 
                    (null != key2._compositeKeyValues) &&
                    String.Equals(key1._entityContainerName, key2._entityContainerName) && 
                    String.Equals(key1._entitySetName, key2._entitySetName) &&
                    (key1._compositeKeyValues.Length == key2._compositeKeyValues.Length) &&
                    ComplexKeyValuesEqual(key1, key2));
        } 

        private static bool ComplexKeyValuesEqual(EntityKey key1, EntityKey key2) 
        { 
            for (int i = 0; i < key1._compositeKeyValues.Length; ++i)
            { 
                if (key1._keyNames[i].Equals(key2._keyNames[i]))
                {
                    if (!Object.Equals(key1._compositeKeyValues[i], key2._compositeKeyValues[i]))
                    { 
                        return false;
                    } 
                } 
                else
                { 
                    // Key names might not be in the same order so try a slower approach that matches
                    // key names between the keys.
                    return KeyValuesEqual(key1, key2);
                } 
            }
            return true; 
        } 

        private static bool KeyValuesEqual(EntityKey key1, EntityKey key2) 
        {
            Dictionary values = new Dictionary();
            foreach (EntityKeyMember key1Value in key1.EntityKeyValues)
            { 
                values.Add(key1Value.Key, key1Value.Value);
            } 
            foreach (EntityKeyMember key2Value in key2.EntityKeyValues) 
            {
                object value; 
                if (!values.TryGetValue(key2Value.Key, out value) || !object.Equals(key2Value.Value, value))
                {
                    return false;
                } 
                values.Remove(key2Value.Key);
            } 
            return true; 
        }
 
        #endregion


        ///  
        /// Returns an array of string/ pairs, one for each key value in this EntityKey,
        /// where the string is the key member name and the DbExpression is the value in this EntityKey 
        /// for that key member, represented as a  with the same result 
        /// type as the key member.
        ///  
        /// The command tree to use to create the key value constant expressions
        /// The entity set to which this EntityKey refers; used to verify that this key has the required key members
        /// The name -> expression mappings for the key member values represented by this EntityKey
        internal KeyValuePair[] GetKeyValueExpressions(DbCommandTree tree, EntitySet entitySet) 
        {
            Debug.Assert(!IsTemporary, "GetKeyValueExpressions doesn't make sense for temporary keys - they have no values."); 
            Debug.Assert(entitySet != null, "GetEntitySet should not return null."); 
            Debug.Assert(entitySet.Name == _entitySetName, "EntitySet returned from GetEntitySet has incorrect name.");
            int numKeyMembers = 0; 
            if (!IsTemporary)
            {
                if (_singletonKeyValue != null)
                { 
                    numKeyMembers = 1;
                } 
                else 
                {
                    numKeyMembers = _compositeKeyValues.Length; 
                }
            }
            if (((EntitySetBase)entitySet).ElementType.KeyMembers.Count != numKeyMembers)
            { 
                // If we found an entity set by name that's a different CLR reference
                // than the one contained by this EntityKey, the two entity sets could 
                // be incompatible.  The only error case we need to handle here is the 
                // one where the number of key members differs; other error cases
                // will be handled by the command tree builder methods. 

                //

 
                throw EntityUtil.EntitySetDoesNotMatch("metadataWorkspace", TypeHelpers.GetFullName(entitySet));
            } 
 
            // Iterate over the internal collection of string->object
            // key value pairs and create a list of string->constant 
            // expression key value pairs.
            KeyValuePair[] keyColumns;
            if (_singletonKeyValue != null)
            { 
                EdmMember singletonKeyMember = ((EntitySetBase)entitySet).ElementType.KeyMembers[0];
                Debug.Assert(singletonKeyMember != null, "Metadata for singleton key member shouldn't be null."); 
                keyColumns = new KeyValuePair[] { 
                    new KeyValuePair(singletonKeyMember.Name,
                        tree.CreateConstantExpression(_singletonKeyValue, Helper.GetModelTypeUsage(singletonKeyMember))) 
                };
            }
            else
            { 
                keyColumns = new KeyValuePair[_compositeKeyValues.Length];
                for (int i = 0; i < _compositeKeyValues.Length; ++i) 
                { 
                    Debug.Assert(_compositeKeyValues[i] != null, "Values within key-value pairs cannot be null.");
 
                    EdmMember keyMember = ((EntitySetBase)entitySet).ElementType.KeyMembers[i];
                    Debug.Assert(keyMember != null, "Metadata for key members shouldn't be null.");
                    keyColumns[i] = new KeyValuePair(keyMember.Name,
                        tree.CreateConstantExpression(_compositeKeyValues[i], Helper.GetModelTypeUsage(keyMember))); 
                }
            } 
 
            return keyColumns;
        } 

        /// 
        /// Returns a string representation of this EntityKey, for use in debugging.
        /// Note that the returned string contains potentially sensitive information 
        /// (i.e., key values), and thus shouldn't be publicly exposed.
        ///  
        internal string ConcatKeyValue() 
        {
            System.Text.StringBuilder builder = new System.Text.StringBuilder(); 
            builder.Append("EntitySet=").Append(_entitySetName);
            if (!IsTemporary)
            {
                foreach (EntityKeyMember pair in EntityKeyValues) 
                {
                    builder.Append(';'); 
                    builder.Append(pair.Key).Append("=").Append(pair.Value); 
                }
 
            }
            return builder.ToString();
        }
 
        /// 
        /// Returns the appropriate value for the given key name. 
        ///  
        internal object FindValueByName(string keyName)
        { 
            Debug.Assert(!IsTemporary, "FindValueByName should not be called for temporary keys.");
            if (SingletonKeyValue != null)
            {
                Debug.Assert(_keyNames[0] == keyName, "For a singleton key, the given keyName must match."); 
                return _singletonKeyValue;
            } 
            else 
            {
                object[] compositeKeyValues = CompositeKeyValues; 
                for (int i = 0; i < compositeKeyValues.Length; i++)
                {
                    if (keyName == _keyNames[i])
                    { 
                        return compositeKeyValues[i];
                    } 
                } 
                throw EntityUtil.ArgumentOutOfRange("keyName");
            } 
        }

        internal static void GetEntitySetName(string qualifiedEntitySetName, out  string entitySet, out string container)
        { 
            entitySet = null;
            container = null; 
            EntityUtil.CheckStringArgument(qualifiedEntitySetName, "qualifiedEntitySetName"); 

            string[] result = qualifiedEntitySetName.Split('.'); 
            if (result.Length != 2)
            {
                throw EntityUtil.InvalidQualifiedEntitySetName();
            } 

            container = result[0]; 
            entitySet = result[1]; 

            // both parts must be non-empty 
            if (container == null || container.Length == 0 ||
                entitySet == null || entitySet.Length == 0)
            {
                throw EntityUtil.InvalidQualifiedEntitySetName(); 
            }
 
            ValidateName(container); 
            ValidateName(entitySet);
        } 

        internal static void ValidateName(string name)
        {
            if (!System.Data.EntityModel.SchemaObjectModel.Utils.ValidUndottedName(name)) 
            {
                throw EntityUtil.EntityKeyInvalidName(name); 
            } 
        }
 
        #region Key Value Assignment and Validation

        private static void CheckKeyValues(EntitySet entitySet, IEnumerable> entityKeyValues,
            out string[] keyNames, out object singletonKeyValue, out object[] compositeKeyValues) 
        {
            Debug.Assert(entitySet != null, "EntitySet should not be null."); 
            EntityUtil.CheckArgumentNull(entityKeyValues, "entityKeyValues"); 

 
            int numExpectedKeyValues = 0;
            int numActualKeyValues = 0;
            ReadOnlyMetadataCollection keyMembers = null;
 
            singletonKeyValue = null;
            compositeKeyValues = null; 
 
            // Determine if we're a single or composite key.
            foreach (KeyValuePair value in entityKeyValues) 
            {
                numActualKeyValues++;
            }
 
            keyMembers = ((EntitySetBase)entitySet).ElementType.KeyMembers;
            keyNames = ((EntitySetBase)entitySet).ElementType.KeyMemberNames; 
            numExpectedKeyValues = keyMembers.Count; 
            Debug.Assert(numExpectedKeyValues > 0, "Metadata returned an invalid number of keys.");
            if (numExpectedKeyValues != numActualKeyValues) 
            {
                throw EntityUtil.IncorrectNumberOfKeyValuePairs("entityKeyValues", entitySet.ElementType.FullName, numExpectedKeyValues, numActualKeyValues);
            }
 
            if (numExpectedKeyValues == 1)
            { 
                foreach (KeyValuePair keyValuePair in entityKeyValues) 
                {
                    if (EntityUtil.IsNull(keyValuePair.Value) || String.IsNullOrEmpty(keyValuePair.Key)) 
                    {
                        throw EntityUtil.NoNullsAllowedInKeyValuePairs("entityKeyValues");
                    }
 
                    if (keyNames[0] != keyValuePair.Key)
                    {   // Validate key name 
                        Debug.Assert(null != entitySet, "null entityset"); 
                        throw EntityUtil.MissingKeyValue("entityKeyValues", keyNames[0], entitySet.ElementType.FullName);
                    } 
                    singletonKeyValue = keyValuePair.Value;

                    // Validate type of key value
                    Type entitySetKeyType = ((PrimitiveType)keyMembers[0].TypeUsage.EdmType).ClrEquivalentType; 
                    if (entitySetKeyType != singletonKeyValue.GetType())
                    { 
                        throw EntityUtil.IncorrectValueType("entityKeyValues", keyMembers[0].Name, entitySetKeyType.FullName, singletonKeyValue.GetType().FullName); 
                    }
                } 
            }
            else
            {
                compositeKeyValues = new object[numExpectedKeyValues]; 

                int i = 0; 
                for (i = 0; i < numExpectedKeyValues; ++i) 
                {
                    EdmMember keyField = ((EntitySetBase)entitySet).ElementType.KeyMembers[i]; 
                    bool foundMember = false;

                    foreach (KeyValuePair keyValuePair in entityKeyValues)
                    { 
                        if (keyField.Name == keyValuePair.Key)
                        { 
                            if (EntityUtil.IsNull(keyValuePair.Value) || String.IsNullOrEmpty(keyValuePair.Key)) 
                            {
                                throw EntityUtil.NoNullsAllowedInKeyValuePairs("entityKeyValues"); 
                            }
                            compositeKeyValues[i] = keyValuePair.Value;
                            foundMember = true;
 
                            // Validate type of key values
                            Type entitySetKeyType = ((PrimitiveType)keyField.TypeUsage.EdmType).ClrEquivalentType; 
                            if (entitySetKeyType != compositeKeyValues[i].GetType()) 
                            {
                                throw EntityUtil.IncorrectValueType("entityKeyValues", keyField.Name, entitySetKeyType.FullName, compositeKeyValues[i].GetType().FullName); 
                            }
                            break;
                        }
                    } 
                    // Validate Key Name (if we found it or not)
                    if (!foundMember) 
                    { 
                        throw EntityUtil.MissingKeyValue("entityKeyValues", keyField.Name, entitySet.ElementType.FullName);
                    } 
                }
            }
        }
 
        private static bool CheckKeyValues(IEnumerable> entityKeyValues,
            out string[] keyNames, out object singletonKeyValue, out object[] compositeKeyValues) 
        { 
            return CheckKeyValues(entityKeyValues, false, false, out keyNames, out singletonKeyValue, out compositeKeyValues);
        } 

        private static bool CheckKeyValues(IEnumerable> entityKeyValues, bool allowNullKeys, bool tokenizeStrings,
            out string[] keyNames, out object singletonKeyValue, out object[] compositeKeyValues)
        { 
            EntityUtil.CheckArgumentNull(entityKeyValues, "entityKeyValues");
 
            int numExpectedKeyValues; 
            int numActualKeyValues = 0;
 
            keyNames = null;
            singletonKeyValue = null;
            compositeKeyValues = null;
 
            // Determine if we're a single or composite key.
            foreach (KeyValuePair value in entityKeyValues) 
            { 
                numActualKeyValues++;
            } 

            numExpectedKeyValues = numActualKeyValues;
            if (numExpectedKeyValues == 0)
            { 
                if (!allowNullKeys)
                { 
                    throw EntityUtil.EntityKeyMustHaveValues("entityKeyValues"); 
                }
            } 
            else
            {
                keyNames = new string[numExpectedKeyValues];
 
                if (numExpectedKeyValues == 1)
                { 
                    lock (_nameLookup) 
                    {
                        foreach (KeyValuePair keyValuePair in entityKeyValues) 
                        {
                            if (EntityUtil.IsNull(keyValuePair.Value) || String.IsNullOrEmpty(keyValuePair.Key))
                            {
                                throw EntityUtil.NoNullsAllowedInKeyValuePairs("entityKeyValues"); 
                            }
                            ValidateName(keyValuePair.Key); 
                            keyNames[0] = tokenizeStrings ? EntityKey.LookupSingletonName(keyValuePair.Key) : keyValuePair.Key; 
                            singletonKeyValue = keyValuePair.Value;
                        } 
                    }
                }
                else
                { 
                    compositeKeyValues = new object[numExpectedKeyValues];
 
                    int i = 0; 
                    lock (_nameLookup)
                    { 
                        foreach (KeyValuePair keyValuePair in entityKeyValues)
                        {
                            if (EntityUtil.IsNull(keyValuePair.Value) || String.IsNullOrEmpty(keyValuePair.Key))
                            { 
                                throw EntityUtil.NoNullsAllowedInKeyValuePairs("entityKeyValues");
                            } 
                            Debug.Assert(null == keyNames[i], "shouldn't have a name yet"); 
                            ValidateName(keyValuePair.Key);
                            keyNames[i] = tokenizeStrings ? EntityKey.LookupSingletonName(keyValuePair.Key) : keyValuePair.Key; 
                            compositeKeyValues[i] = keyValuePair.Value;
                            i++;
                        }
                    } 
                }
            } 
            return numExpectedKeyValues > 0; 
        }
 
        /// 
        /// Validates the record parameter passed to the EntityKey constructor,
        /// and converts the data into the form required by EntityKey.  For singleton keys,
        /// this is a single object.  For composite keys, this is an object array. 
        /// 
        /// the entity set metadata object which this key refers to 
        /// the parameter to validate 
        /// the number of expected key-value pairs
        /// the name of the argument to use in exception messages 
        /// the validated value(s) (for a composite key, an object array is returned)
        private static void CheckKeyValues(EntitySet entitySet, IExtendedDataRecord record,
            out string[] keyNames, out object singletonKeyValue, out object[] compositeKeyValues)
        { 
            singletonKeyValue = null;
            compositeKeyValues = null; 
 
            int numExpectedKeyValues = ((EntitySetBase)entitySet).ElementType.KeyMembers.Count;
            keyNames = ((EntitySetBase)entitySet).ElementType.KeyMemberNames; 

            EntityType entityType = record.DataRecordInfo.RecordType.EdmType as EntityType;
            if (entityType == null)
            { 
                throw EntityUtil.DataRecordMustBeEntity();
            } 
 
            // ensure the type contained by this entity set matches the type contained by the data record
            if (entitySet != null && !entitySet.ElementType.IsAssignableFrom(entityType)) 
            {
                throw EntityUtil.EntityTypesDoNotMatch(entityType.Name, entitySet.ElementType.FullName);
            }
            Debug.Assert(numExpectedKeyValues > 0, "Should be expecting a positive number of key-values."); 

            if (numExpectedKeyValues == 1) 
            { 
                // Optimize for a singleton key.
 
                EdmMember member = entityType.KeyMembers[0];
                try
                {
                    singletonKeyValue = CheckValue("record", member.Name, record[member.Name], (PrimitiveType)member.TypeUsage.EdmType); 
                }
                catch (IndexOutOfRangeException ex) 
                { 
                    throw EntityUtil.MissingKeyValue("record", member.Name, entityType.FullName, ex);
                } 
            }
            else
            {
                compositeKeyValues = new object[numExpectedKeyValues]; 
                // grab each key-field from the data record
                for (int i = 0; i < numExpectedKeyValues; ++i) 
                { 
                    EdmMember member = entityType.KeyMembers[i];
                    try 
                    {
                        compositeKeyValues[i] = CheckValue("record", member.Name, record[member.Name],
                            (PrimitiveType)member.TypeUsage.EdmType);
                    } 
                    catch (IndexOutOfRangeException ex)
                    { 
                        throw EntityUtil.MissingKeyValue("record", member.Name, entityType.FullName, ex); 
                    }
                } 
            }
        }

        ///  
        /// Checks whether the given value is valid for the given key field.
        ///  
        /// the name of the argument that contains a value of the given CLR type 
        /// the name of the key field that contains the value of the given CLR type
        /// the value to validate 
        /// the expected type of the value
        /// the validated value
        /// the value is null
        /// the value is of an incorrect type 
        private static object CheckValue(string argumentName, string keyFieldName, object value, PrimitiveType expectedType)
        { 
            Debug.Assert(expectedType != null, "expectedType cannot be null"); 
            if (EntityUtil.IsNull(value))
            { 
                throw EntityUtil.NoNullsAllowedInKeyValuePairs(argumentName);
            }
            if (expectedType.ClrEquivalentType != value.GetType())
            { 
                throw EntityUtil.IncorrectValueType(argumentName, keyFieldName, expectedType.ClrEquivalentType.FullName, value.GetType().FullName);
            } 
            return value; 
        }
        ///  
        /// Verify that the types of the objects passed in to be used as keys actually match the types from the model.
        /// This error is also caught when the entity is materialized and when the key value is set, at which time it
        /// also throws ThrowSetInvalidValue().
        /// SQLBUDT 513838. This error is possible and should be caught at run time, not in an assertion. 
        /// 
        /// The EntitySet to validate against 
        internal void ValidateEntityKey(EntitySet entitySet) 
        {
            ValidateEntityKey(entitySet, false, null); 
        }
        /// 
        /// Verify that the types of the objects passed in to be used as keys actually match the types from the model.
        /// This error is also caught when the entity is materialized and when the key value is set, at which time it 
        /// also throws ThrowSetInvalidValue().
        /// SQLBUDT 513838. This error is possible and should be caught at run time, not in an assertion. 
        ///  
        /// The EntitySet to validate against
        /// Type of the exception to throw 
        internal void ValidateEntityKey(EntitySet entitySet, bool isArgumentException, string argumentName)
        {
            if (entitySet != null)
            { 
                ReadOnlyMetadataCollection keyMembers = ((EntitySetBase)entitySet).ElementType.KeyMembers;
                if (_singletonKeyValue != null) 
                { 
                    // 1. Validate number of keys
                    if (keyMembers.Count != 1) 
                    {
                        if (isArgumentException)
                        {
                            throw EntityUtil.IncorrectNumberOfKeyValuePairs(argumentName, entitySet.ElementType.FullName, keyMembers.Count, 1); 
                        }
                        else 
                        { 
                            throw EntityUtil.IncorrectNumberOfKeyValuePairsInvalidOperation(entitySet.ElementType.FullName, keyMembers.Count, 1);
                        } 
                    }

                    // 2. Validate type of key values
                    Type entitySetKeyType = ((PrimitiveType)keyMembers[0].TypeUsage.EdmType).ClrEquivalentType; 
                    if (entitySetKeyType != _singletonKeyValue.GetType())
                    { 
                        if (isArgumentException) 
                        {
                            throw EntityUtil.IncorrectValueType(argumentName, keyMembers[0].Name, entitySetKeyType.FullName, _singletonKeyValue.GetType().FullName); 
                        }
                        else
                        {
                            throw EntityUtil.IncorrectValueTypeInvalidOperation(keyMembers[0].Name, entitySetKeyType.FullName, _singletonKeyValue.GetType().FullName); 
                        }
                    } 
 
                    // 3. Validate key names
                    if (_keyNames[0] != keyMembers[0].Name) 
                    {
                        if (isArgumentException)
                        {
                            throw EntityUtil.MissingKeyValue(argumentName, keyMembers[0].Name, entitySet.ElementType.FullName); 
                        }
                        else 
                        { 
                            throw EntityUtil.MissingKeyValueInvalidOperation(keyMembers[0].Name, entitySet.ElementType.FullName);
                        } 
                    }
                }
                else if (null != _compositeKeyValues)
                { 
                    // 1. Validate number of keys
                    if (keyMembers.Count != _compositeKeyValues.Length) 
                    { 
                        if (isArgumentException)
                        { 
                            throw EntityUtil.IncorrectNumberOfKeyValuePairs(argumentName, entitySet.ElementType.FullName, keyMembers.Count, _compositeKeyValues.Length);
                        }
                        else
                        { 
                            throw EntityUtil.IncorrectNumberOfKeyValuePairsInvalidOperation(entitySet.ElementType.FullName, keyMembers.Count, _compositeKeyValues.Length);
                        } 
                    } 

                    for (int i = 0; i < _compositeKeyValues.Length; ++i) 
                    {
                        EdmMember keyField = ((EntitySetBase)entitySet).ElementType.KeyMembers[i];
                        bool foundMember = false;
                        for(int j = 0; j < _compositeKeyValues.Length; ++j) 
                        {
                            if (keyField.Name == _keyNames[j]) 
                            { 
                                // 2. Validate type of key values
                                Type entitySetKeyType = ((PrimitiveType)keyField.TypeUsage.EdmType).ClrEquivalentType; 
                                if (entitySetKeyType != _compositeKeyValues[j].GetType())
                                {
                                    if (isArgumentException)
                                    { 
                                        throw EntityUtil.IncorrectValueType(argumentName, keyField.Name, entitySetKeyType.FullName, _compositeKeyValues[j].GetType().FullName);
                                    } 
                                    else 
                                    {
                                        throw EntityUtil.IncorrectValueTypeInvalidOperation(keyField.Name, entitySetKeyType.FullName, _compositeKeyValues[j].GetType().FullName); 
                                    }
                                }
                                foundMember = true;
                                break; 
                            }
                        } 
                        // 3. Validate Key Name (if we found it or not) 
                        if (!foundMember)
                        { 
                            if (isArgumentException)
                            {
                                throw EntityUtil.MissingKeyValue(argumentName, keyField.Name, entitySet.ElementType.FullName);
                            } 
                            else
                            { 
                                throw EntityUtil.MissingKeyValueInvalidOperation(keyField.Name, entitySet.ElementType.FullName); 
                            }
                        } 
                    }
                }
            }
        } 

        ///  
        /// Asserts that the "state" of the EntityKey is correct, by validating assumptions 
        /// based on whether the key is a singleton, composite, or temporary.
        ///  
        /// whether we expect this EntityKey to be marked temporary
        [Conditional("DEBUG")]
        private void AssertCorrectState(EntitySetBase entitySet, bool isTemporary)
        { 
            if (_singletonKeyValue != null)
            { 
                Debug.Assert(!isTemporary, "Singleton keys should not be expected to be temporary."); 
                Debug.Assert(_compositeKeyValues == null, "The EntityKey is marked as both a singleton key and a composite key - this is illegal.");
                if (entitySet != null) 
                {
                    Debug.Assert(entitySet.ElementType.KeyMembers.Count == 1, "For a singleton key, the number of key fields must be exactly 1.");
                }
            } 
            else if (_compositeKeyValues != null)
            { 
                Debug.Assert(!isTemporary, "Composite keys should not be expected to be temporary."); 
                if (entitySet != null)
                { 
                    Debug.Assert(entitySet.ElementType.KeyMembers.Count > 1, "For a compsite key, the number of key fields should be greater than 1.");
                    Debug.Assert(entitySet.ElementType.KeyMembers.Count == _compositeKeyValues.Length, "Incorrect number of values specified to composite key.");
                }
                for (int i = 0; i < _compositeKeyValues.Length; ++i) 
                {
                    Debug.Assert(_compositeKeyValues[i] != null, "Values passed to a composite EntityKey cannot be null."); 
                } 
            }
            else if (!IsTemporary) 
            {
                // one of our static keys
                Debug.Assert(!isTemporary, "Static keys should not be expected to be temporary.");
                Debug.Assert(this.EntityKeyValues == null, "The EntityKeyValues property for Static EntityKeys must return null."); 
                Debug.Assert(this.EntityContainerName == null, "The EntityContainerName property for Static EntityKeys must return null.");
                Debug.Assert(this.EntitySetName != null, "The EntitySetName property for Static EntityKeys must not return null."); 
            } 
            else
            { 
                Debug.Assert(isTemporary, "The EntityKey is marked as neither a singleton or composite.  Therefore, it should be expected to be temporary.");
                Debug.Assert(this.IsTemporary, "The EntityKey is marked as neither a singleton or composite.  Therefore it must be marked as temporary.");
                Debug.Assert(this.EntityKeyValues == null, "The EntityKeyValues property for temporary EntityKeys must return null.");
            } 
        }
 
        #endregion 

        #region Serialization 

        /// 
        ///
        ///  
        /// 
        [OnDeserializing] 
        public void OnDeserializing(StreamingContext context) 
        {
            if (RequiresDeserialization) 
            {
                DeserializeMembers();
            }
        } 

        ///  
        /// 
        /// 
        ///  
        [OnDeserialized]
        public void OnDeserialized(StreamingContext context)
        {
            lock (_nameLookup) 
            {
                _entitySetName = LookupSingletonName(_entitySetName); 
                _entityContainerName = LookupSingletonName(_entityContainerName); 
                if (_keyNames != null)
                { 
                    for (int i = 0; i < _keyNames.Length; i++)
                    {
                        _keyNames[i] = LookupSingletonName(_keyNames[i]);
                    } 
                }
            } 
        } 

        ///  
        /// Dev Note: this must be called from within a _lock block on _nameLookup
        /// 
        /// 
        ///  
        private static string LookupSingletonName(string name)
        { 
            if (String.IsNullOrEmpty(name)) 
            {
                return null; 
            }
            if (_nameLookup.ContainsKey(name))
            {
                return _nameLookup[name]; 
            }
            _nameLookup.Add(name, name); 
            return name; 
        }
 
        private void ValidateWritable(object instance)
        {
            if (_isLocked || instance != null)
            { 
                throw EntityUtil.CannotChangeEntityKey();
            } 
        } 

        private bool RequiresDeserialization 
        {
            get { return _deserializedMembers != null; }
        }
 
        private void DeserializeMembers()
        { 
            if (CheckKeyValues(new KeyValueReader(_deserializedMembers), true, true, out _keyNames, out _singletonKeyValue, out _compositeKeyValues)) 
            {
                // If we received values from the _deserializedMembers, then we do not need to track these any more 
                _deserializedMembers = null;
            }
        }
 
        #endregion
 
        private class KeyValueReader : IEnumerable> 
        {
            IEnumerable _enumerator; 

            public KeyValueReader(IEnumerable enumerator)
            {
                _enumerator = enumerator; 
            }
 
            #region IEnumerable> Members 

            public IEnumerator> GetEnumerator() 
            {
                foreach (EntityKeyMember pair in _enumerator)
                {
                    if (pair != null) 
                    {
                        yield return new KeyValuePair(pair.Key, pair.Value); 
                    } 
                }
            } 

            #endregion

            #region IEnumerable Members 

            IEnumerator IEnumerable.GetEnumerator() 
            { 
                return this.GetEnumerator();
            } 

            #endregion
        }
    } 

    ///  
    /// Information about a key that is part of an EntityKey. 
    /// A key member contains the key name and value.
    ///  
    [DataContract]
    [Serializable]
    public class EntityKeyMember
    { 
        private string _keyName;
        private object _keyValue; 
 
        /// 
        /// Creates an empty EntityKeyMember. This constructor is used by serialization. 
        /// 
        public EntityKeyMember()
        {
        } 

        ///  
        /// Creates a new EntityKeyMember with the specified key name and value. 
        /// 
        /// The key name 
        /// The key value
        public EntityKeyMember(string keyName, object keyValue)
        {
            EntityUtil.CheckArgumentNull(keyName, "keyName"); 
            EntityUtil.CheckArgumentNull(keyValue, "keyValue");
            _keyName = keyName; 
            _keyValue = keyValue; 
        }
 
        /// 
        /// The key name
        /// 
        [DataMember] 
        public string Key
        { 
            get 
            {
                return _keyName; 
            }
            set
            {
                ValidateWritable(_keyName); 
                EntityUtil.CheckArgumentNull(value, "value");
                _keyName = value; 
            } 
        }
 
        /// 
        /// The key value
        /// 
        [DataMember] 
        public object Value
        { 
            get 
            {
                return _keyValue; 
            }
            set
            {
                ValidateWritable(_keyValue); 
                EntityUtil.CheckArgumentNull(value, "value");
                _keyValue = value; 
            } 
        }
 
        /// 
        /// Returns a string representation of the EntityKeyMember
        /// 
        /// A string representation of the EntityKeyMember 
        public override string ToString()
        { 
            return String.Format(System.Globalization.CultureInfo.CurrentCulture, "[{0}, {1}]", _keyName, _keyValue); 
        }
 
        /// 
        /// Ensures that the instance can be written to (value must be null)
        /// 
        private void ValidateWritable(object instance) 
        {
            if (instance != null) 
            { 
                throw EntityUtil.CannotChangeEntityKey();
            } 
        }
    }
}
 

// 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