// <summary>
        // Finds interesting members for MetdataWorkspace.GetRequiredOriginalValueMembers() and MetdataWorkspace.GetRelevantMembersForUpdate() methods
        // for the given <paramref name="entitySet" /> and <paramref name="entityType" />.
        // </summary>
        // <param name="entitySet"> An EntitySet belonging to the C-Space. Must not be null. </param>
        // <param name="entityType"> An EntityType that participates in the given EntitySet. Must not be null. </param>
        // <param name="interestingMembersKind"> Scenario the members should be returned for. </param>
        // <returns>
        // ReadOnlyCollection of interesting members for the requested scenario (
        // <paramref
        //     name="interestingMembersKind" />
        // ).
        // </returns>
        private ReadOnlyCollection<EdmMember> FindInterestingMembers(
            EntitySetBase entitySet, EntityTypeBase entityType, InterestingMembersKind interestingMembersKind)
        {
            DebugCheck.NotNull(entitySet);
            DebugCheck.NotNull(entityType);

            var interestingMembers = new List<EdmMember>();

            foreach (
                var storageTypeMapping in
                    MappingMetadataHelper.GetMappingsForEntitySetAndSuperTypes(this, entitySet.EntityContainer, entitySet, entityType))
            {
                var associationTypeMapping = storageTypeMapping as AssociationTypeMapping;
                if (associationTypeMapping != null)
                {
                    FindInterestingAssociationMappingMembers(associationTypeMapping, interestingMembers);
                }
                else
                {
                    Debug.Assert(storageTypeMapping is EntityTypeMapping, "EntityTypeMapping expected.");

                    FindInterestingEntityMappingMembers(
                        (EntityTypeMapping)storageTypeMapping, interestingMembersKind, interestingMembers);
                }
            }

            // For backwards compatibility we don't return foreign keys from the obsolete MetadataWorkspace.GetRequiredOriginalValueMembers() method
            if (interestingMembersKind != InterestingMembersKind.RequiredOriginalValueMembers)
            {
                FindForeignKeyProperties(entitySet, entityType, interestingMembers);
            }

            foreach (var functionMappings in MappingMetadataHelper
                .GetModificationFunctionMappingsForEntitySetAndType(this, entitySet.EntityContainer, entitySet, entityType)
                .Where(functionMappings => functionMappings.UpdateFunctionMapping != null))
            {
                FindInterestingFunctionMappingMembers(functionMappings, interestingMembersKind, ref interestingMembers);
            }

            Debug.Assert(interestingMembers != null, "interestingMembers must never be null.");

            return new ReadOnlyCollection<EdmMember>(interestingMembers.Distinct().ToList());
        }
        // <summary>
        // Finds interesting entity properties - primary keys (if requested), properties (including complex properties and nested properties)
        // with concurrency mode set to fixed and C-Side condition members and adds them to the
        // <paramref
        //     name="interestingMembers" />
        // .
        // </summary>
        // <param name="entityTypeMapping"> Entity type mapping. Must not be null. </param>
        // <param name="interestingMembersKind"> Scenario the members should be returned for. </param>
        // <param name="interestingMembers"> The list the interesting members (if any) will be added to. Must not be null. </param>
        private static void FindInterestingEntityMappingMembers(
            EntityTypeMapping entityTypeMapping, InterestingMembersKind interestingMembersKind, List<EdmMember> interestingMembers)
        {
            DebugCheck.NotNull(entityTypeMapping);
            DebugCheck.NotNull(interestingMembers);

            foreach (var propertyMapping in entityTypeMapping.MappingFragments.SelectMany(mf => mf.AllProperties))
            {
                var scalarPropMapping = propertyMapping as ScalarPropertyMapping;
                var complexPropMapping = propertyMapping as ComplexPropertyMapping;
                var conditionMapping = propertyMapping as ConditionPropertyMapping;

                Debug.Assert(!(propertyMapping is EndPropertyMapping), "association mapping properties should be handled elsewhere.");

                Debug.Assert(
                    scalarPropMapping != null ||
                    complexPropMapping != null ||
                    conditionMapping != null, "Unimplemented property mapping");

                //scalar property
                if (scalarPropMapping != null
                    && scalarPropMapping.Property != null)
                {
                    // (0) if a member is part of the key it is interesting
                    if (MetadataHelper.IsPartOfEntityTypeKey(scalarPropMapping.Property))
                    {
                        // For backwards compatibility we do return primary keys from the obsolete MetadataWorkspace.GetRequiredOriginalValueMembers() method
                        if (interestingMembersKind == InterestingMembersKind.RequiredOriginalValueMembers)
                        {
                            interestingMembers.Add(scalarPropMapping.Property);
                        }
                    }
                    //(3) if a scalar property has Fixed concurrency mode then it is "interesting"
                    else if (MetadataHelper.GetConcurrencyMode(scalarPropMapping.Property)
                             == ConcurrencyMode.Fixed)
                    {
                        interestingMembers.Add(scalarPropMapping.Property);
                    }
                }
                else if (complexPropMapping != null)
                {
                    // (7) All complex members - partial update scenarios only
                    // (3.1) The complex property or its one of its children has fixed concurrency mode
                    if (interestingMembersKind == InterestingMembersKind.PartialUpdate
                        ||
                        MetadataHelper.GetConcurrencyMode(complexPropMapping.Property) == ConcurrencyMode.Fixed
                        || HasFixedConcurrencyModeInAnyChildProperty(complexPropMapping))
                    {
                        interestingMembers.Add(complexPropMapping.Property);
                    }
                }
                else if (conditionMapping != null)
                {
                    //(1) C-Side condition members are 'interesting'
                    if (conditionMapping.Property != null)
                    {
                        interestingMembers.Add(conditionMapping.Property);
                    }
                }
            }
        }
        // <summary>
        // Finds interesting members for modification functions mapped to stored procedures and adds them to the
        // <paramref
        //     name="interestingMembers" />
        // .
        // </summary>
        // <param name="functionMappings"> Modification function mapping. Must not be null. </param>
        // <param name="interestingMembersKind"> Update scenario the members will be used in (in general - partial update vs. full update). </param>
        private static void FindInterestingFunctionMappingMembers(
            EntityTypeModificationFunctionMapping functionMappings, InterestingMembersKind interestingMembersKind,
            ref List<EdmMember> interestingMembers)
        {
            DebugCheck.NotNull(functionMappings);
            DebugCheck.NotNull(functionMappings.UpdateFunctionMapping);
            DebugCheck.NotNull(interestingMembers);

            // for partial update scenarios (e.g. EntityDataSourceControl) all members are interesting otherwise the data may be corrupt. 
            // See bugs #272992 and #124460 in DevDiv database for more details. For full update scenarios and the obsolete 
            // MetadataWorkspace.GetRequiredOriginalValueMembers() metod we return only members with Version set to "Original".
            if (interestingMembersKind == InterestingMembersKind.PartialUpdate)
            {
                // (5) Members included in Update ModificationFunction
                interestingMembers.AddRange(
                    functionMappings.UpdateFunctionMapping.ParameterBindings.Select(p => p.MemberPath.Members.Last()));
            }
            else
            {
                //(4) Members in update ModificationFunction with Version="Original" are "interesting"
                // This also works when you have complex-types (4.1)

                Debug.Assert(
                    interestingMembersKind == InterestingMembersKind.FullUpdate
                    || interestingMembersKind == InterestingMembersKind.RequiredOriginalValueMembers,
                    "Unexpected kind of interesting members - if you changed the InterestingMembersKind enum type update this code accordingly");

                foreach (var parameterBinding in functionMappings.UpdateFunctionMapping.ParameterBindings.Where(p => !p.IsCurrent))
                {
                    //Last is the root element (with respect to the Entity)
                    //For example,  Entity1={
                    //                  S1, 
                    //                  C1{S2, 
                    //                     C2{ S3, S4 } 
                    //                     }, 
                    //                  S5}
                    // if S4 matches (i.e. C1.C2.S4), then it returns C1
                    //because internally the list is [S4][C2][C1]
                    interestingMembers.Add(parameterBinding.MemberPath.Members.Last());
                }
            }
        }
        // <summary>
        // Return members for MetdataWorkspace.GetRequiredOriginalValueMembers() and MetdataWorkspace.GetRelevantMembersForUpdate() methods.
        // </summary>
        // <param name="entitySet"> An EntitySet belonging to the C-Space. Must not be null. </param>
        // <param name="entityType"> An EntityType that participates in the given EntitySet. Must not be null. </param>
        // <param name="interestingMembersKind"> Scenario the members should be returned for. </param>
        // <returns>
        // ReadOnlyCollection of interesting members for the requested scenario (
        // <paramref
        //     name="interestingMembersKind" />
        // ).
        // </returns>
        internal ReadOnlyCollection<EdmMember> GetInterestingMembers(
            EntitySetBase entitySet, EntityTypeBase entityType, InterestingMembersKind interestingMembersKind)
        {
            DebugCheck.NotNull(entitySet);
            DebugCheck.NotNull(entityType);

            var key = new Tuple<EntitySetBase, EntityTypeBase, InterestingMembersKind>(entitySet, entityType, interestingMembersKind);
            return _cachedInterestingMembers.GetOrAdd(key, FindInterestingMembers(entitySet, entityType, interestingMembersKind));
        }
        /// <summary>
        ///     Return members for MetdataWorkspace.GetRequiredOriginalValueMembers() and MetdataWorkspace.GetRelevantMembersForUpdate() methods.
        /// </summary>
        /// <param name="entitySet"> An EntitySet belonging to the C-Space. Must not be null. </param>
        /// <param name="entityType"> An EntityType that participates in the given EntitySet. Must not be null. </param>
        /// <param name="interestingMembersKind"> Scenario the members should be returned for. </param>
        /// <returns> ReadOnlyCollection of interesting members for the requested scenario ( <paramref
        ///      name="interestingMembersKind" /> ). </returns>
        internal ReadOnlyCollection<EdmMember> GetInterestingMembers(
            EntitySetBase entitySet, EntityTypeBase entityType, InterestingMembersKind interestingMembersKind)
        {
            Debug.Assert(entitySet != null, "entitySet != null");
            Debug.Assert(entityType != null, "entityType != null");

            var key = new Tuple<EntitySetBase, EntityTypeBase, InterestingMembersKind>(entitySet, entityType, interestingMembersKind);
            return _cachedInterestingMembers.GetOrAdd(key, FindInterestingMembers(entitySet, entityType, interestingMembersKind));
        }
        /// <summary>
        /// Finds interesting members for modification functions mapped to stored procedures and adds them to the <paramref name="interestingMembers"/>.
        /// </summary>
        /// <param name="functionMappings">Modification function mapping. Must not be null.</param>
        /// <param name="interestingMembersKind">Update scenario the members will be used in (in general - partial update vs. full update).</param>
        /// <param name="interestingMembers"></param>
        private static void FindInterestingFunctionMappingMembers(StorageEntityTypeModificationFunctionMapping functionMappings, InterestingMembersKind interestingMembersKind, ref List <EdmMember> interestingMembers)
        {
            Debug.Assert(functionMappings != null && functionMappings.UpdateFunctionMapping != null, "Expected function mapping fragment with non-null update function mapping");
            Debug.Assert(interestingMembers != null, "interestingMembers != null");

            // for partial update scenarios (e.g. EntityDataSourceControl) all members are interesting otherwise the data may be corrupt.
            // See bugs #272992 and #124460 in DevDiv database for more details. For full update scenarios and the obsolete
            // MetadataWorkspace.GetRequiredOriginalValueMembers() metod we return only members with Version set to "Original".
            if (interestingMembersKind == InterestingMembersKind.PartialUpdate)
            {
                // (5) Members included in Update ModificationFunction
                interestingMembers.AddRange(functionMappings.UpdateFunctionMapping.ParameterBindings.Select(p => p.MemberPath.Members.Last()));
            }
            else
            {
                //(4) Members in update ModificationFunction with Version="Original" are "interesting"
                // This also works when you have complex-types (4.1)

                Debug.Assert(
                    interestingMembersKind == InterestingMembersKind.FullUpdate || interestingMembersKind == InterestingMembersKind.RequiredOriginalValueMembers,
                    "Unexpected kind of interesting members - if you changed the InterestingMembersKind enum type update this code accordingly");

                foreach (var parameterBinding in functionMappings.UpdateFunctionMapping.ParameterBindings.Where(p => !p.IsCurrent))
                {
                    //Last is the root element (with respect to the Entity)
                    //For example,  Entity1={
                    //                  S1,
                    //                  C1{S2,
                    //                     C2{ S3, S4 }
                    //                     },
                    //                  S5}
                    // if S4 matches (i.e. C1.C2.S4), then it returns C1
                    //because internally the list is [S4][C2][C1]
                    interestingMembers.Add(parameterBinding.MemberPath.Members.Last());
                }
            }
        }
        /// <summary>
        /// Finds interesting entity properties - primary keys (if requested), properties (including complex properties and nested properties)
        /// with concurrency mode set to fixed and C-Side condition members and adds them to the <paramref name="interestingMembers"/>.
        /// </summary>
        /// <param name="entityTypeMapping">Entity type mapping. Must not be null.</param>
        /// <param name="interestingMembersKind">Scenario the members should be returned for.</param>
        /// <param name="interestingMembers">The list the interesting members (if any) will be added to. Must not be null.</param>
        private static void FindInterestingEntityMappingMembers(StorageEntityTypeMapping entityTypeMapping, InterestingMembersKind interestingMembersKind, List <EdmMember> interestingMembers)
        {
            Debug.Assert(entityTypeMapping != null, "entityTypeMapping != null");
            Debug.Assert(interestingMembers != null, "interestingMembers != null");

            foreach (var propertyMapping in entityTypeMapping.MappingFragments.SelectMany(mf => mf.AllProperties))
            {
                StorageScalarPropertyMapping    scalarPropMapping  = propertyMapping as StorageScalarPropertyMapping;
                StorageComplexPropertyMapping   complexPropMapping = propertyMapping as StorageComplexPropertyMapping;
                StorageConditionPropertyMapping conditionMapping   = propertyMapping as StorageConditionPropertyMapping;

                Debug.Assert(!(propertyMapping is StorageEndPropertyMapping), "association mapping properties should be handled elsewhere.");

                Debug.Assert(scalarPropMapping != null ||
                             complexPropMapping != null ||
                             conditionMapping != null, "Unimplemented property mapping");

                //scalar property
                if (scalarPropMapping != null && scalarPropMapping.EdmProperty != null)
                {
                    // (0) if a member is part of the key it is interesting
                    if (MetadataHelper.IsPartOfEntityTypeKey(scalarPropMapping.EdmProperty))
                    {
                        // For backwards compatibility we do return primary keys from the obsolete MetadataWorkspace.GetRequiredOriginalValueMembers() method
                        if (interestingMembersKind == InterestingMembersKind.RequiredOriginalValueMembers)
                        {
                            interestingMembers.Add(scalarPropMapping.EdmProperty);
                        }
                    }
                    //(3) if a scalar property has Fixed concurrency mode then it is "interesting"
                    else if (MetadataHelper.GetConcurrencyMode(scalarPropMapping.EdmProperty) == ConcurrencyMode.Fixed)
                    {
                        interestingMembers.Add(scalarPropMapping.EdmProperty);
                    }
                }
                else if (complexPropMapping != null)
                {
                    // (7) All complex members - partial update scenarios only
                    // (3.1) The complex property or its one of its children has fixed concurrency mode
                    if (interestingMembersKind == InterestingMembersKind.PartialUpdate ||
                        MetadataHelper.GetConcurrencyMode(complexPropMapping.EdmProperty) == ConcurrencyMode.Fixed || HasFixedConcurrencyModeInAnyChildProperty(complexPropMapping))
                    {
                        interestingMembers.Add(complexPropMapping.EdmProperty);
                    }
                }
                else if (conditionMapping != null)
                {
                    //(1) C-Side condition members are 'interesting'
                    if (conditionMapping.EdmProperty != null)
                    {
                        interestingMembers.Add(conditionMapping.EdmProperty);
                    }
                }
            }
        }
        /// <summary>
        /// Finds interesting members for MetdataWorkspace.GetRequiredOriginalValueMembers() and MetdataWorkspace.GetRelevantMembersForUpdate() methods
        /// for the given <paramref name="entitySet"/> and <paramref name="entityType"/>.
        /// </summary>
        /// <param name="entitySet">An EntitySet belonging to the C-Space. Must not be null.</param>
        /// <param name="entityType">An EntityType that participates in the given EntitySet. Must not be null.</param>
        /// <param name="interestingMembersKind">Scenario the members should be returned for.</param>
        /// <returns>ReadOnlyCollection of interesting members for the requested scenario (<paramref name="interestingMembersKind"/>).</returns>
        private ReadOnlyCollection <EdmMember> FindInterestingMembers(EntitySetBase entitySet, EntityTypeBase entityType, InterestingMembersKind interestingMembersKind)
        {
            Debug.Assert(entitySet != null, "entitySet != null");
            Debug.Assert(entityType != null, "entityType != null");

            var interestingMembers = new List <EdmMember>();

            foreach (var storageTypeMapping in MappingMetadataHelper.GetMappingsForEntitySetAndSuperTypes(this, entitySet.EntityContainer, entitySet, entityType))
            {
                StorageAssociationTypeMapping associationTypeMapping = storageTypeMapping as StorageAssociationTypeMapping;
                if (associationTypeMapping != null)
                {
                    FindInterestingAssociationMappingMembers(associationTypeMapping, interestingMembers);
                }
                else
                {
                    Debug.Assert(storageTypeMapping is StorageEntityTypeMapping, "StorageEntityTypeMapping expected.");

                    FindInterestingEntityMappingMembers((StorageEntityTypeMapping)storageTypeMapping, interestingMembersKind, interestingMembers);
                }
            }

            // For backwards compatibility we don't return foreign keys from the obsolete MetadataWorkspace.GetRequiredOriginalValueMembers() method
            if (interestingMembersKind != InterestingMembersKind.RequiredOriginalValueMembers)
            {
                FindForeignKeyProperties(entitySet, entityType, interestingMembers);
            }

            foreach (var functionMappings in MappingMetadataHelper
                     .GetModificationFunctionMappingsForEntitySetAndType(this, entitySet.EntityContainer, entitySet, entityType)
                     .Where(functionMappings => functionMappings.UpdateFunctionMapping != null))
            {
                FindInterestingFunctionMappingMembers(functionMappings, interestingMembersKind, ref interestingMembers);
            }

            Debug.Assert(interestingMembers != null, "interestingMembers must never be null.");

            return(new ReadOnlyCollection <EdmMember>(interestingMembers.Distinct().ToList()));
        }
        /// <summary>
        /// Return members for MetdataWorkspace.GetRequiredOriginalValueMembers() and MetdataWorkspace.GetRelevantMembersForUpdate() methods.
        /// </summary>
        /// <param name="entitySet">An EntitySet belonging to the C-Space. Must not be null.</param>
        /// <param name="entityType">An EntityType that participates in the given EntitySet. Must not be null.</param>
        /// <param name="interestingMembersKind">Scenario the members should be returned for.</param>
        /// <returns>ReadOnlyCollection of interesting members for the requested scenario (<paramref name="interestingMembersKind"/>).</returns>
        internal ReadOnlyCollection <EdmMember> GetInterestingMembers(EntitySetBase entitySet, EntityTypeBase entityType, InterestingMembersKind interestingMembersKind)
        {
            Debug.Assert(entitySet != null, "entitySet != null");
            Debug.Assert(entityType != null, "entityType != null");

            var key = new Tuple <EntitySetBase, EntityTypeBase, InterestingMembersKind>(entitySet, entityType, interestingMembersKind);

            return(_cachedInterestingMembers.GetOrAdd(key, FindInterestingMembers(entitySet, entityType, interestingMembersKind)));
        }