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) { @@ -182,6 +193,94 @@ namespace Marr.Data InitOneToManyChildLists(entityRef); } + /// + /// Searches for a previously loaded parent entity and then sets that reference to the mapped Relationship property. + /// + public void AddParentReference() + { + 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, + /// or false if the values are the same. + /// The currently concatenated keys are saved in the LastKeyGroup property. + /// + /// + /// + public bool IsNewGroup(DbDataReader reader) + { + bool isNewGroup = false; + + // Get primary keys from parent entity and any one-to-one child entites + GroupingKeyCollection groupingKeyColumns = this.GroupingKeyColumns; + + // Concatenate column values + KeyGroupInfo keyGroupInfo = groupingKeyColumns.CreateGroupingKey(reader); + + if (!keyGroupInfo.HasNullKey && !_entityReferences.ContainsKey(keyGroupInfo.GroupingKey)) + { + isNewGroup = true; + } + + return isNewGroup; + } + + /// + /// Gets the GroupingKeys for this entity. + /// GroupingKeys determine when to create and add a new entity to the graph. + /// + /// + /// A simple entity with no relationships will return only its PrimaryKey columns. + /// A parent entity with one-to-one child relationships will include its own PrimaryKeys, + /// and it will recursively traverse all Children with one-to-one relationships and add their PrimaryKeys. + /// A child entity that has a one-to-one relationship with its parent will use the same + /// GroupingKeys already defined by its parent. + /// + public GroupingKeyCollection GroupingKeyColumns + { + get + { + if (_groupingKeyColumns == null) + _groupingKeyColumns = GetGroupingKeyColumns(); + + return _groupingKeyColumns; + } + } + + 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. /// @@ -213,71 +312,6 @@ namespace Marr.Data } } - /// - /// Recursively adds primary key columns from contiguous child graphs with a one-to-one relationship type to the pKeys collection.. - /// - /// - /// - private void AddOneToOneChildKeys(ColumnMapCollection pKeys, EntityGraph entity) - { - 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); - } - } - - /// - /// Concatenates the values of the GroupingKeys property and compares them - /// against the LastKeyGroup property. Returns true if the values are different, - /// or false if the values are the same. - /// The currently concatenated keys are saved in the LastKeyGroup property. - /// - /// - /// - public bool IsNewGroup(DbDataReader reader) - { - bool isNewGroup = false; - - // Get primary keys from parent entity and any one-to-one child entites - GroupingKeyCollection groupingKeyColumns = this.GroupingKeyColumns; - - // Concatenate column values - KeyGroupInfo keyGroupInfo = groupingKeyColumns.CreateGroupingKey(reader); - - if (!keyGroupInfo.HasNullKey && !_entityReferences.ContainsKey(keyGroupInfo.GroupingKey)) - { - isNewGroup = true; - } - - return isNewGroup; - } - - /// - /// Gets the GroupingKeys for this entity. - /// GroupingKeys determine when to create and add a new entity to the graph. - /// - /// - /// A simple entity with no relationships will return only its PrimaryKey columns. - /// A parent entity with one-to-one child relationships will include its own PrimaryKeys, - /// and it will recursively traverse all Children with one-to-one relationships and add their PrimaryKeys. - /// A child entity that has a one-to-one relationship with its parent will use the same - /// GroupingKeys already defined by its parent. - /// - public GroupingKeyCollection GroupingKeyColumns - { - get - { - if (_groupingKeyColumns == null) - _groupingKeyColumns = GetGroupingKeyColumns(); - - return _groupingKeyColumns; - } - } - /// /// 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