Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataEntity / System / Data / EntityKey.cs / 2 / 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, IEnumerableentityKeyValues) { 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/ /// 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 ///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 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) { ReadOnlyMetadataCollectionkeyMembers = ((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, IEnumerableentityKeyValues) { 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/ /// 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 ///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 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) { ReadOnlyMetadataCollectionkeyMembers = ((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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlSchemaObjectTable.cs
- CacheSection.cs
- BamlRecordHelper.cs
- TextRunCache.cs
- XmlILIndex.cs
- SettingsPropertyValue.cs
- WebBrowsableAttribute.cs
- NonSerializedAttribute.cs
- ObjectDataSourceMethodEventArgs.cs
- WindowsFormsHostAutomationPeer.cs
- HeaderCollection.cs
- ColumnPropertiesGroup.cs
- DoubleCollectionValueSerializer.cs
- Constraint.cs
- PathGradientBrush.cs
- PerformanceCounterLib.cs
- WebConfigurationHostFileChange.cs
- TabControlCancelEvent.cs
- InputLanguageManager.cs
- DesignerDataSchemaClass.cs
- BindableTemplateBuilder.cs
- ColumnResizeAdorner.cs
- DirtyTextRange.cs
- CmsInterop.cs
- ToolBar.cs
- PasswordTextContainer.cs
- PropertyDescriptor.cs
- Attribute.cs
- BookmarkOptionsHelper.cs
- MenuEventArgs.cs
- ListSortDescriptionCollection.cs
- ContextTokenTypeConverter.cs
- ExceptionHandlers.cs
- SoapElementAttribute.cs
- MouseCaptureWithinProperty.cs
- SettingsContext.cs
- controlskin.cs
- DataStorage.cs
- FixedDSBuilder.cs
- StylusTouchDevice.cs
- MetadataItemEmitter.cs
- ExceptionUtil.cs
- MetadataItemEmitter.cs
- EndPoint.cs
- ScriptingRoleServiceSection.cs
- DataGridHeaderBorder.cs
- WebPartConnectionCollection.cs
- SHA256.cs
- ChangesetResponse.cs
- MappedMetaModel.cs
- ModifierKeysValueSerializer.cs
- WebPartRestoreVerb.cs
- DrawingImage.cs
- OutOfProcStateClientManager.cs
- Comparer.cs
- SiteIdentityPermission.cs
- SQLBytesStorage.cs
- AutoGeneratedField.cs
- Vector3DKeyFrameCollection.cs
- DataGridAutoFormatDialog.cs
- UncommonField.cs
- QilTargetType.cs
- ResourceProperty.cs
- AttachedAnnotationChangedEventArgs.cs
- SchemaImporterExtension.cs
- ClientUrlResolverWrapper.cs
- ValidatorCollection.cs
- WindowProviderWrapper.cs
- RectAnimationClockResource.cs
- DataGridViewTopRowAccessibleObject.cs
- itemelement.cs
- UpdateException.cs
- KeyInstance.cs
- TiffBitmapEncoder.cs
- ByteStorage.cs
- OdbcParameter.cs
- QuaternionConverter.cs
- SynchronousChannelMergeEnumerator.cs
- CallbackHandler.cs
- CollectionBuilder.cs
- SafeArrayRankMismatchException.cs
- JulianCalendar.cs
- SimpleType.cs
- TypeDependencyAttribute.cs
- ObjectIDGenerator.cs
- TraceSwitch.cs
- XmlText.cs
- XamlClipboardData.cs
- FragmentNavigationEventArgs.cs
- tabpagecollectioneditor.cs
- FontWeightConverter.cs
- TypedRowGenerator.cs
- SmtpReplyReaderFactory.cs
- WMIGenerator.cs
- MgmtConfigurationRecord.cs
- StylusPointPropertyInfo.cs
- HtmlInputSubmit.cs
- MapPathBasedVirtualPathProvider.cs
- CacheAxisQuery.cs
- TargetParameterCountException.cs