File: System\Data\EntityModel\SchemaObjectModel\EntityContainerAssociationSet.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="EntityContainerAssociationSet.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.EntityModel.SchemaObjectModel
{
    using System.Collections.Generic;
    using System.Data.Metadata.Edm;
    using System.Data.Objects.DataClasses;
    using System.Diagnostics;
    using System.Globalization;
    using System.Xml;
 
    /// <summary>
    /// Represents an AssociationSet element.
    /// </summary>
    internal sealed class EntityContainerAssociationSet : EntityContainerRelationshipSet
    {
        // Note: If you add more fields, please make sure you handle that in the clone method
        private Dictionary<string, EntityContainerAssociationSetEnd> _relationshipEnds = new Dictionary<string, EntityContainerAssociationSetEnd>();
        private List<EntityContainerAssociationSetEnd> _rolelessEnds = new List<EntityContainerAssociationSetEnd>();
 
        /// <summary>
        /// Constructs an EntityContainerAssociationSet
        /// </summary>
        /// <param name="parentElement">Reference to the schema element.</param>
        public EntityContainerAssociationSet( EntityContainer parentElement )
            : base( parentElement )
        {
        }
 
 
        /// <summary>
        /// The ends defined and infered for this AssociationSet
        /// </summary>
        internal override IEnumerable<EntityContainerRelationshipSetEnd> Ends
        {
            get 
            {
                foreach ( EntityContainerAssociationSetEnd end in _relationshipEnds.Values )
                {
                    yield return end;
                }
 
                foreach ( EntityContainerAssociationSetEnd end in _rolelessEnds )
                {
                    yield return end;
                }
            }
        }
 
        protected override bool HandleAttribute(XmlReader reader)
        {
            if (base.HandleAttribute(reader))
            {
                return true;
            }
            else if (CanHandleAttribute(reader, XmlConstants.Association))
            {
                HandleRelationshipTypeNameAttribute(reader);
                return true;
            }
 
            return false;
        }
 
        protected override bool HandleElement(XmlReader reader)
        {
            if (base.HandleElement(reader))
            {
                return true;
            }
            else if (CanHandleElement(reader, XmlConstants.End))
            {
                HandleEndElement(reader);
                return true;
            }
            return false;
        }
 
        /// <summary>
        /// The method that is called when an End element is encountered.
        /// </summary>
        /// <param name="reader">The XmlReader positioned at the EndElement.</param>
        private void HandleEndElement( XmlReader reader )
        {
            Debug.Assert( reader != null );
 
            EntityContainerAssociationSetEnd end = new EntityContainerAssociationSetEnd( this );
            end.Parse( reader );
 
            if ( end.Role == null  )
            {
                // we will resolve the role name later and put it in the 
                // normal _relationshipEnds dictionary
                _rolelessEnds.Add( end );
                return;
            }
 
            if ( HasEnd( end.Role ) )
            {
                end.AddError( ErrorCode.InvalidName, EdmSchemaErrorSeverity.Error, reader,
                    System.Data.Entity.Strings.DuplicateEndName(end.Name ) );
                return;
            }
 
            _relationshipEnds.Add( end.Role, end );
        }
 
        internal override void ResolveTopLevelNames()
        {
            base.ResolveTopLevelNames();
 
            // this just got resolved
            Debug.Assert(
                Relationship == null || Relationship.RelationshipKind == RelationshipKind.Association,
                string.Format(CultureInfo.InvariantCulture, "The relationship referenced by the Association attribute of {0} is not an Association relationship.", FQName));
        }
 
        internal override void ResolveSecondLevelNames()
        {
            base.ResolveSecondLevelNames();
            // the base class should have fixed up the role names on my ends
            foreach (EntityContainerAssociationSetEnd end in _rolelessEnds)
            {
                if (end.Role != null)
                {
                    if (HasEnd(end.Role))
                    {
                        end.AddError(ErrorCode.InvalidName, EdmSchemaErrorSeverity.Error,
                            System.Data.Entity.Strings.InferRelationshipEndGivesAlreadyDefinedEnd(end.EntitySet.FQName, Name));
                    }
                    else
                    {
                        _relationshipEnds.Add(end.Role, end);
                    }
                }
 
                // any that didn't get resolved will already have errors entered
            }
 
            _rolelessEnds.Clear();
        }
 
        /// <summary>
        /// Create and add a EntityContainerEnd from the IRelationshipEnd provided
        /// </summary>
        /// <param name="relationshipEnd">The relationship end of the end to add.</param>
        /// <param name="entitySet">The entitySet to associate with the relationship end.</param>
        protected override void AddEnd( IRelationshipEnd relationshipEnd, EntityContainerEntitySet entitySet )
        {
            Debug.Assert( relationshipEnd != null );
            Debug.Assert( !_relationshipEnds.ContainsKey( relationshipEnd.Name ) );
            // we expect set to be null sometimes
 
            EntityContainerAssociationSetEnd end = new EntityContainerAssociationSetEnd( this );
            end.Role = relationshipEnd.Name;
            end.RelationshipEnd = relationshipEnd;
 
            end.EntitySet = entitySet;
            if ( end.EntitySet != null )
            {
                _relationshipEnds.Add( end.Role, end );
            }
        }
 
        protected override bool HasEnd( string role )
        {
            return _relationshipEnds.ContainsKey( role );
        }
 
        internal override SchemaElement Clone(SchemaElement parentElement)
        {
            EntityContainerAssociationSet associationSet = new EntityContainerAssociationSet((EntityContainer)parentElement);
 
            associationSet.Name = this.Name;
            associationSet.Relationship = this.Relationship;
 
            foreach (EntityContainerAssociationSetEnd end in this.Ends)
            {
                EntityContainerAssociationSetEnd clonedEnd = (EntityContainerAssociationSetEnd)end.Clone(associationSet);
                associationSet._relationshipEnds.Add(clonedEnd.Role, clonedEnd);
            }
 
            return associationSet;
        }
    }
}