File: System\Data\Metadata\Edm\NavigationProperty.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="NavigationProperty.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.Threading;
using System.Linq;
 
namespace System.Data.Metadata.Edm
{
    /// <summary>
    /// Represent the edm navigation property class
    /// </summary>
    public sealed class NavigationProperty : EdmMember
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the navigation property class
        /// </summary>
        /// <param name="name">name of the navigation property</param>
        /// <param name="typeUsage">TypeUsage object containing the navigation property type and its facets</param>
        /// <exception cref="System.ArgumentNullException">Thrown if name or typeUsage arguments are null</exception>
        /// <exception cref="System.ArgumentException">Thrown if name argument is empty string</exception>
        internal NavigationProperty(string name, TypeUsage typeUsage)
            : base(name, typeUsage)
        {
            EntityUtil.CheckStringArgument(name, "name");
            EntityUtil.GenericCheckArgumentNull(typeUsage, "typeUsage");
            _accessor = new NavigationPropertyAccessor(name);
        }
 
        /// <summary>
        /// Initializes a new OSpace instance of the property class
        /// </summary>
        /// <param name="name">name of the property</param>
        /// <param name="typeUsage">TypeUsage object containing the property type and its facets</param>
        /// <param name="propertyInfo">for the property</param>
        internal NavigationProperty(string name, TypeUsage typeUsage, System.Reflection.PropertyInfo propertyInfo)
            : this(name, typeUsage)
        {
            System.Diagnostics.Debug.Assert(name == propertyInfo.Name, "different PropertyName?");
            if (null != propertyInfo)
            {
                System.Reflection.MethodInfo method;
                
                method = propertyInfo.GetGetMethod();
                PropertyGetterHandle = ((null != method) ? method.MethodHandle : default(System.RuntimeMethodHandle));
            }
        }
        #endregion
 
        /// <summary>
        /// Returns the kind of the type
        /// </summary>
        public override BuiltInTypeKind BuiltInTypeKind { get { return BuiltInTypeKind.NavigationProperty; } }
 
        #region Fields
        internal const string RelationshipTypeNamePropertyName = "RelationshipType";
        internal const string ToEndMemberNamePropertyName = "ToEndMember";
        private RelationshipType _relationshipType;
        private RelationshipEndMember _toEndMember;
        private RelationshipEndMember _fromEndMember;
 
        /// <summary>Store the handle, allowing the PropertyInfo/MethodInfo/Type references to be GC'd</summary>
        internal readonly System.RuntimeMethodHandle PropertyGetterHandle;
 
        /// <summary>cached dynamic methods to access the property values from a CLR instance</summary> 
        private readonly NavigationPropertyAccessor _accessor;
        #endregion
 
        /// <summary>
        /// Gets/Sets the relationship type that this navigation property operates on
        /// </summary>
        /// <exception cref="System.InvalidOperationException">Thrown if the NavigationProperty instance is in ReadOnly state</exception>
        [MetadataProperty(BuiltInTypeKind.RelationshipType, false)]
        public RelationshipType RelationshipType
        {
            get
            {
                return _relationshipType;
            }
            internal set
            {
                _relationshipType = value;
            }
        }
 
        /// <summary>
        /// Gets/Sets the to relationship end member in the navigation
        /// </summary>
        /// <exception cref="System.InvalidOperationException">Thrown if the NavigationProperty instance is in ReadOnly state</exception>
        [MetadataProperty(BuiltInTypeKind.RelationshipEndMember, false)]
        public RelationshipEndMember ToEndMember
        {
            get
            {
                return _toEndMember;
            }
            internal set
            {
                _toEndMember = value;
            }
        }
 
        /// <summary>
        /// Gets/Sets the from relationship end member in the navigation
        /// </summary>
        /// <exception cref="System.InvalidOperationException">Thrown if the NavigationProperty instance is in ReadOnly state</exception>
        [MetadataProperty(BuiltInTypeKind.RelationshipEndMember, false)]
        public RelationshipEndMember FromEndMember
        {
            get
            {
                return _fromEndMember;
            }
            internal set
            {
                _fromEndMember = value;
            }
        }
 
        internal NavigationPropertyAccessor Accessor
        {
            get { return _accessor; }
        }
 
        /// <summary>
        /// Where the given navigation property is on the dependent end of a referential constraint,
        /// returns the foreign key properties. Otherwise, returns an empty set. We will return the members in the order
        /// of the principal end key properties.
        /// </summary>
        /// <returns>Foreign key properties</returns>
        public IEnumerable<EdmProperty> GetDependentProperties()
        {
            // Get the declared type
            AssociationType associationType = (AssociationType)this.RelationshipType;
            Debug.Assert(
                         associationType.ReferentialConstraints != null,
                         "ReferenceConstraints cannot be null");
 
            if (associationType.ReferentialConstraints.Count > 0)
            {
                ReferentialConstraint rc = associationType.ReferentialConstraints[0];
                RelationshipEndMember dependentEndMember = rc.ToRole;
 
                if (dependentEndMember.EdmEquals(this.FromEndMember))
                {
                    //Order the dependant properties in the order of principal end's key members.
                    var keyMembers = rc.FromRole.GetEntityType().KeyMembers;
                    var dependantProperties = new List<EdmProperty>(keyMembers.Count);
                    for (int i = 0; i < keyMembers.Count; i++)
                    {
                        dependantProperties.Add(rc.ToProperties[rc.FromProperties.IndexOf(((EdmProperty)keyMembers[i]))]);
                    }
                    return dependantProperties.AsReadOnly();
                }
            }
            
            return Enumerable.Empty<EdmProperty>();
        }
    }
}