/// <summary> /// Calculate count of principal self navigation properties for /// ordering to be able to successfully define order. /// </summary> /// <remarks> /// If Category has principal navigation to itself named ParentCategory, we need to /// find count of them to be able to define order appropriately. We need to define state of /// them in ascending order. /// </remarks> /// <example> /// firstCategory which has null value on ParentCategory property has 0 principal self /// navigation count. If ParentCategory of secondCategory is firstCategory, then secondCategory /// has 1 principal self navigation count, if ParentCategory of thirdCateogry is secondCategory, /// then it has 2 principal self navigaiton count. /// </example> /// <typeparam name="TEntity">Type of entity.</typeparam> /// <param name="entity">Entity to find principla self navigaiton count.</param> /// <param name="store">Store to add calculated count and check first.</param> /// <returns>Count of principal self navigation count.</returns> private int CalculatePrincipalSelfNavigationCount <TEntity>( TEntity entity, Dictionary <object, int> store) where TEntity : class { if (store.ContainsKey(entity)) { return(store[entity]); } string typeName = entity.GetType().Name; // Get navigation details and find properties which is refers to itslef. IGraphEntityManager <TEntity> entityManager = GetEntityManager <TEntity>(); NavigationDetail navigationDetail = entityManager.GetNavigationDetail(); List <string> selfReferringPrincipalPropertyNames = navigationDetail .Relations .Where(m => m.Direction == NavigationDirection.From && m.PropertyTypeName == typeName) .Select(m => m.PropertyName) .ToList(); // Loop through principal navigation properties and add their count. int principalSelfCount = 0; foreach (string propertyName in selfReferringPrincipalPropertyNames) { TEntity principalEntity = entity.GetPropertyValue(propertyName) as TEntity; // If principla entity is null then continue if (principalEntity == null) { continue; } // Othewise calculate principal count as 1 + count for principal entity principalSelfCount++; principalSelfCount += CalculatePrincipalSelfNavigationCount(principalEntity, store); } store.Add(entity, principalSelfCount); return(principalSelfCount); }
/// <summary> /// Add all child or parents entities related to given entity /// and entity itself to the relatedEntityList. /// </summary> /// <exception cref="ArgumentNullException"> /// When entity or relatedEntityList is null. /// </exception> /// <typeparam name="TEntity">Type of entity.</typeparam> /// <param name="entity">Entity to get all related entities.</param> /// <param name="relatedEntityList">List of entities to add.</param> public void GetAllEntities <TEntity>( TEntity entity, List <object> relatedEntityList) where TEntity : class { if (entity == null) { throw new ArgumentNullException(nameof(entity)); } if (relatedEntityList == null) { throw new ArgumentNullException(nameof(relatedEntityList)); } if (!relatedEntityList.Contains(entity)) { relatedEntityList.Add(entity); } IGraphEntityManager <TEntity> graphEntityManager = GetEntityManager <TEntity>(); NavigationDetail navigationDetail = graphEntityManager .GetNavigationDetail(); List <PropertyInfo> navigationPropeties = navigationDetail .Relations .Select(n => entity.GetProperty(n.PropertyName)) .ToList(); if (navigationPropeties != null && navigationPropeties.Count > 0) { foreach (PropertyInfo childEntityProperty in navigationPropeties) { if (childEntityProperty.PropertyType.IsCollectionType()) { // If child entity is collection get all entities inside this collection. IEnumerable <object> enumerableChildEntity = entity.GetPropertyValue(childEntityProperty.Name) as IEnumerable <object>; if (enumerableChildEntity != null) { for (int i = 0; i < enumerableChildEntity.Count(); i++) { dynamic childEntity = enumerableChildEntity.ElementAt(i); if (childEntity != null && !relatedEntityList.Contains(childEntity)) { GetAllEntities( childEntity, relatedEntityList); } } } } else { // If child entity is not collection get its own. dynamic childEntity = entity.GetPropertyValue(childEntityProperty.Name); if (childEntity != null && !relatedEntityList.Contains(childEntity)) { GetAllEntities( childEntity, relatedEntityList); } } } } }
/// <summary> /// Set property of parent entity to targetValue /// which has property value equals to currentValue. /// </summary> /// <typeparam name="TEntity">Type of entity.</typeparam> /// <param name="currentValue">Current value of property to replace.</param> /// <param name="targetValue">Target value to set value of property of parent.</param> private void ReplaceEntitiesInParents <TEntity>( TEntity currentValue, TEntity targetValue) where TEntity : class { IGraphEntityManager <TEntity> graphEntityManager = GetEntityManager <TEntity>(); NavigationDetail navigationDetailOfCurrent = graphEntityManager .GetNavigationDetail(); // Get parent properties of entity. // Properties which have navigation property type of which // is type of entity. Ignore navigation properties // which are mutual navigation properties with entity itself. string typeName = currentValue.GetType().Name; List <NavigationDetail> parentNavigationDetails = GetNavigationDetails() .Select(n => new NavigationDetail() { SourceTypeName = n.SourceTypeName, Relations = n.Relations .Where(r => r.PropertyTypeName.Equals(typeName) && r.SourceMultiplicity == RelationshipMultiplicity.Many && !navigationDetailOfCurrent .Relations .Any(c => c.PropertyTypeName.Equals(n.SourceTypeName) && c.TargetMultiplicity == r.SourceMultiplicity && c.FromKeyNames.SequenceEqual(r.FromKeyNames) && c.ToKeyNames.SequenceEqual(r.ToKeyNames))) .ToList() }) .Where(n => n.Relations != null && n.Relations.Count() > 0) .ToList(); // Get assembly to be able to get types according to type name Assembly entityAssembly = currentValue.GetType().Assembly; if (parentNavigationDetails != null && parentNavigationDetails.Count() > 0) { foreach (NavigationDetail parentNavigation in parentNavigationDetails) { Type parentType = entityAssembly.GetTypes() .FirstOrDefault(t => t.Name.Equals(parentNavigation.SourceTypeName)); // Get local set of parent IEnumerable <object> localParentSet = Context .Set(parentType) .Local .CastToGeneric(); foreach (NavigationRelation navigationRelation in parentNavigation.Relations) { PropertyInfo childProperty = parentType.GetProperty(navigationRelation.PropertyName); if (!childProperty.PropertyType.IsCollectionType()) { // Get all parent entities which have current entity inside var containerParentCollection = localParentSet .Where(m => m.GetPropertyValue(navigationRelation.PropertyName) != null && m.GetPropertyValue(navigationRelation.PropertyName).Equals(currentValue)) .ToList(); // If collection is empty then skip. if (containerParentCollection == null || containerParentCollection.Count == 0) { continue; } foreach (var containerParent in containerParentCollection) { // If parent is null then skip. if (containerParent == null) { continue; } // If parent with property value of currentValue found replace values /* * If duplicate entity is in the entity, state of which has already * been defined and if state of this parent entity is Unchanged or * Modified, trying to change navigation property of this parent entity * will throw InvalidOperationException with following message: * "A referential integrity constraint violation occurred: * A primary key property that is a part of referential integrity constraint * cannot be changed when the dependent object is Unchanged unless it is being * set to the association's principal object. The principal object must * be tracked and not marked for deletion." * As a workaround I am storing current state of parent entity, changing the * state to Added, then replacing duplicate entity and in the end * I set state of parent entity to stored current state. */ // Store current state var currentState = Context.Entry(containerParent).State; // Change state to added Context.Entry(containerParent).State = EntityState.Added; // Replace value through // context.Entry(containerParent).Member(propertyName).CurrentValue // because otherwise EntityFramework will not be able to track entities Context.Entry(containerParent) .Member(navigationRelation.PropertyName).CurrentValue = targetValue; // Restore state to current state Context.Entry(containerParent).State = currentState; } } } } } }