diff --git a/Marr.Data/DataMapper.cs b/Marr.Data/DataMapper.cs index 8ad948eae..d35194f1f 100644 --- a/Marr.Data/DataMapper.cs +++ b/Marr.Data/DataMapper.cs @@ -660,15 +660,19 @@ namespace Marr.Data // and multiple entities are created from each view record. foreach (EntityGraph lvl in graph) { - // If is child relationship entity, and childrenToLoad are specified, and entity is not listed, - // then skip this entity. - if (childrenToLoad.Count > 0 && !lvl.IsRoot && !childrenToLoad.ContainsMember(lvl.Member)) // lvl.Member.Name + if (lvl.IsParentReference) { + // A child specified a circular reference to its previously loaded parent + lvl.AddParentReference(); + } + else if (childrenToLoad.Count > 0 && !lvl.IsRoot && !childrenToLoad.ContainsMember(lvl.Member)) + { + // A list of relationships-to-load was specified and this relationship was not included continue; } - - if (lvl.IsNewGroup(reader)) + else if (lvl.IsNewGroup(reader)) { + // Create a new entity with the data reader var newEntity = mappingHelper.CreateAndLoadEntity(lvl.EntityType, lvl.Columns, reader, true); // Add entity to the appropriate place in the object graph diff --git a/Marr.Data/EntityGraph.cs b/Marr.Data/EntityGraph.cs index b28fbc69c..ce9e55e9f 100644 --- a/Marr.Data/EntityGraph.cs +++ b/Marr.Data/EntityGraph.cs @@ -43,7 +43,8 @@ namespace Marr.Data private GroupingKeyCollection _groupingKeyColumns; private Dictionary _entityReferences; - public IList RootList { get; private set; } + internal IList RootList { get; private set; } + internal bool IsParentReference { get; private set; } /// /// Recursively builds an entity graph of the given parent type. @@ -68,12 +69,22 @@ namespace Marr.Data _entityType = entityType; _parent = parent; _relationship = relationship; - _columns = _repos.GetColumns(entityType); + IsParentReference = !IsRoot && AnyParentsAreOfType(entityType); + if (!IsParentReference) + { + _columns = _repos.GetColumns(entityType); + } + _relationships = _repos.GetRelationships(entityType); _children = new List(); Member = relationship != null ? relationship.Member : null; _entityReferences = new Dictionary(); + if (IsParentReference) + { + return; + } + // Create a new EntityGraph for each child relationship that is not lazy loaded foreach (Relationship childRelationship in this.Relationships) { @@ -183,53 +194,14 @@ namespace Marr.Data } /// - /// Initializes the owning lists on many-to-many Children. - /// - /// - private void InitOneToManyChildLists(EntityReference entityRef) - { - // Get a reference to the parent's the childrens' OwningLists to the parent entity - for (int i = 0; i < Relationships.Count; i++) - { - Relationship relationship = Relationships[i]; - if (relationship.RelationshipInfo.RelationType == RelationshipTypes.Many) - { - try - { - IList list = (IList)_repos.ReflectionStrategy.CreateInstance(relationship.MemberType); - _repos.ReflectionStrategy.SetFieldValue(entityRef.Entity, relationship.Member.Name, list); - - // Save a reference to each 1-M list - entityRef.AddChildList(relationship.Member.Name, list); - } - catch (Exception ex) - { - throw new DataMappingException( - string.Format("{0}.{1} is a \"Many\" relationship type so it must derive from IList.", - entityRef.Entity.GetType().Name, relationship.Member.Name), - ex); - } - } - } - } - - /// - /// Recursively adds primary key columns from contiguous child graphs with a one-to-one relationship type to the pKeys collection.. + /// Searches for a previously loaded parent entity and then sets that reference to the mapped Relationship property. /// - /// - /// - private void AddOneToOneChildKeys(ColumnMapCollection pKeys, EntityGraph entity) + public void AddParentReference() { - var oneToOneChildren = entity.Children - .Where(c => c._relationship.RelationshipInfo.RelationType == RelationshipTypes.One); - - foreach (var child in oneToOneChildren) - { - pKeys.AddRange(child.Columns.PrimaryKeys); - AddOneToOneChildKeys(pKeys, child); - } + var parentReference = FindParentReference(); + _repos.ReflectionStrategy.SetFieldValue(_parent._entity, _relationship.Member.Name, parentReference); } - + /// /// Concatenates the values of the GroupingKeys property and compares them /// against the LastKeyGroup property. Returns true if the values are different, @@ -247,7 +219,7 @@ namespace Marr.Data // Concatenate column values KeyGroupInfo keyGroupInfo = groupingKeyColumns.CreateGroupingKey(reader); - + if (!keyGroupInfo.HasNullKey && !_entityReferences.ContainsKey(keyGroupInfo.GroupingKey)) { isNewGroup = true; @@ -255,7 +227,7 @@ namespace Marr.Data return isNewGroup; } - + /// /// Gets the GroupingKeys for this entity. /// GroupingKeys determine when to create and add a new entity to the graph. @@ -278,6 +250,68 @@ namespace Marr.Data } } + private bool AnyParentsAreOfType(Type type) + { + EntityGraph parent = _parent; + while (parent != null) + { + if (parent._entityType == type) + { + return true; + } + parent = parent._parent; + } + + return false; + } + + private object FindParentReference() + { + var parent = this.Parent.Parent; + while (parent != null) + { + if (parent._entityType == _relationship.MemberType) + { + return parent._entity; + } + + parent = parent.Parent; + } + + return null; + } + + /// + /// Initializes the owning lists on many-to-many Children. + /// + /// + private void InitOneToManyChildLists(EntityReference entityRef) + { + // Get a reference to the parent's the childrens' OwningLists to the parent entity + for (int i = 0; i < Relationships.Count; i++) + { + Relationship relationship = Relationships[i]; + if (relationship.RelationshipInfo.RelationType == RelationshipTypes.Many) + { + try + { + IList list = (IList)_repos.ReflectionStrategy.CreateInstance(relationship.MemberType); + _repos.ReflectionStrategy.SetFieldValue(entityRef.Entity, relationship.Member.Name, list); + + // Save a reference to each 1-M list + entityRef.AddChildList(relationship.Member.Name, list); + } + catch (Exception ex) + { + throw new DataMappingException( + string.Format("{0}.{1} is a \"Many\" relationship type so it must derive from IList.", + entityRef.Entity.GetType().Name, relationship.Member.Name), + ex); + } + } + } + } + /// /// Gets a list of keys to group by. /// @@ -373,4 +407,4 @@ public struct KeyGroupInfo { get { return _hasNullKey; } } -} \ No newline at end of file +} diff --git a/Marr.Data/MapRepository.cs b/Marr.Data/MapRepository.cs index 682fc4729..c60dc4c09 100644 --- a/Marr.Data/MapRepository.cs +++ b/Marr.Data/MapRepository.cs @@ -54,7 +54,7 @@ namespace Marr.Data // Register a default IReflectionStrategy ReflectionStrategy = new CachedReflectionStrategy(); - + // Register a default type converter for Enums TypeConverters.Add(typeof(Enum), new Converters.EnumStringConverter()); @@ -180,7 +180,7 @@ namespace Marr.Data return Relationships[type]; } - + #endregion #region - Reflection Strategy - diff --git a/Marr.Data/Marr.Data.csproj b/Marr.Data/Marr.Data.csproj index 060887697..5018ad393 100644 --- a/Marr.Data/Marr.Data.csproj +++ b/Marr.Data/Marr.Data.csproj @@ -12,10 +12,6 @@ Marr.Data v4.0 512 - SAK - SAK - SAK - SAK 3.5